Réalisez un assistant de présaisie pour alimenter la base d'un serveur central

Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Avant-propos

Ce document a pour but de vous montrer comment concevoir un assistant de présaisie des données permettant à un panel d'utilisateurs de saisir les informations nécessaires à la mise à jour d'une base de données centrale située sur un serveur distant.

L'idée de ce tutoriel est née d'une situation existante pour laquelle j'ai été confronté à concevoir le même prototype d'outil que j'ai ici largement simplifié pour ne pas rendre la mise en œuvre trop complexe, notamment pour les débutants.

I-A. Niveau

Vous devez être relativement à l'aise avec Microsoft Access et connaître la conception avancée de formulaires, mais également la manipulation des requêtes avec Visual Basic, en ayant une bonne approche de ce langage afin de mettre en pratique cet exemple.

Ce tutoriel s'adresse plus particulièrement aux personnes qui possèdent déjà de bonnes notions concernant la gestion des événements avec Visual Basic sur Microsoft Access incluant la manipulation des données à l'aide de DAO.

I-B. Contact

Pour tous renseignements complémentaires, veuillez me contacter directement (Langue Argyronet) par MP.

Si votre question est, disons publique et technique, merci de poster un message dans le forum afin d'en faire partager le contenu…

I-C. Le projet proposé

Le projet abordé dans ce tutoriel est fourni sous forme de code source à titre d'exemple qui, une fois compilé, assemblé et interprété peut nécessiter des adaptations ou des modifications en rapport avec vos besoins.

Il est tout à fait possible que votre vision des choses à l'égard de la méthodologie employée au sein des différentes fonctions ou procédures soit différente de la mienne. Aussi, n'hésitez pas à proposer vos idées si vous pensez qu'elles peuvent apporter un plus à ce projet.

Notez que j'ai cherché avant tout à simplifier l'ensemble pour que tout un chacun puisse l'assimiler de façon aisée.

I-D. Contexte métier

Le projet présenté ici concerne des données relatives à des prestations de service supervisées par certains sites pour certaines communes. Ces prestations sont soumises à des contrats nantis d'une durée de validité. Libre à vous de vous en inspirer pour votre propre situation métier ou tout autre développement particulier.

II. Pourquoi ce projet

II-A. Contexte

Ce tutoriel est né, comme bien souvent en ce qui me concerne, de l'idée d'une réalisation professionnelle qui m'a été demandée et que j'ai souhaité partager avec vous, du fait je la trouve intéressante à exploiter.

J'espère que ce sera le cas pour vous…

Pour clarifier un peu les choses, le projet présenté ici ne peut être mis en application que si vous êtes dans une situation où un besoin de centralisation des données est nécessaire, et qu'il faut préparer les données du serveur d'une part, mais aussi que votre application de gestion soit exploitée par différents sites où l'activité professionnelle (pour le cas présenté ici) exerce sur les communes spécifiques, ces dernières étant associées ou non à des contrats.

Bien entendu, il faut considérer que la présentation de ce projet reste surtout et avant tout théorique, car je suis bien conscient qu'il est difficile de se mettre dans une situation identique à celle qui m'a poussé à créer cet assistant.

Ceci dit, l'objectif est surtout de comprendre les points culminants de ce tutoriel à savoir :

  • voir comment il est relativement aisé d'enchaîner dynamiquement l'ouverture de formulaires à l'image de ce que vous rencontrez lorsque vous lancez un programme d'installation ;
  • approfondir vos connaissances concernant plus particulièrement l'usage de procédures génériques associées à des événements, qui évite ainsi la redondance de code et donc par voie de conséquence, une maintenance plus aisée ;
  • voir également comment il est possible de vérifier qu'un sous-formulaire est complété comme attendu au moment de la saisie d'une part, et au moment de la validation globale d'autre part. J'entends par validation globale, les comportements associés au passage à l'écran précédent ou à l'écran suivant qui se traduit par le fait de conserver ou non le contenu (confirmé par un message).

D'une manière générale, du fait que cet assistant est destiné à des non-informaticiens, on fait en sorte de rendre les messages affichés qui concernent la sauvegarde, le soient de manière optimiste. En d'autres termes, si l'utilisateur n'est pas en mesure de renseigner une valeur, qu'il ne soit pas obligé de le faire.

II-B. De quoi avez-vous besoin ?

Pour mettre en œuvre ce projet, vous n'avez besoin que de Microsoft Access dans une version supérieure ou égale à la version 2000. Il n'y a pas de référence particulière autre que celles définies par défaut dans Microsoft Access lors de la création de nouveaux projets.

Libre à vous de préférer DAO 3.6 ou le nouveau moteur existant depuis la version 2007.

III. Présentation du projet

Le projet est ici et expressément une base de données Access non scindée. Je précise cela, car il est plus facile de distribuer une application dans cette configuration d'une part, et cela évite les problèmes d'installation d'autre part. Par ailleurs, du fait du côté volatil de l'application si je puis dire, il n'est pas nécessaire de s'investir dans une structure frontale/dorsale.

III-A. Structure fonctionnelle des formulaires

Dans le projet présenté ici, les éléments prépondérants sont bien entendu les formulaires. Ils sont au nombre de onze. Parmi ces formulaires, figure un formulaire d'accueil qui propose et énumère ce qui va être fait et un formulaire de fin qui récapitule la saisie.

Entre les deux, figurent tous les autres formulaires de saisie des données utilisateur qu'ils soient formulaires simples ou continus.

Concernant les formulaires de saisie, chacun d'entre eux est pourvu de boutons qui permettent de créer ou de supprimer un enregistrement, et également, d'accéder au formulaire précédent ou au formulaire suivant.

Pour chacun des formulaires qui contiennent des sous-formulaires, sont présentes deux étiquettes qui pour l'une, permet de savoir si le formulaire et dûment complété ou non et pour l'autre, le nombre d'enregistrements existants.

Il est rappelé ici que les écrans doivent être construits de telle sorte à ce qu'ils soient compris par des personnes qui n'ont pas l'habitude d'utiliser l'outil informatique et à qui l'on demande de saisir des informations avec un mode qualifié de simple.

IV. Les tables du projet

Au niveau des tables, du fait que ce projet concerne dans son côté métier l'exploitation de prestations de services par rapport à des communes, certaines tables contiennent des valeurs prédéfinies.

Dans l'exemple présenté, trois tables contiennent respectivement l'ensemble des communes, des départements et des régions pour la France.

Les autres tables contiennent des valeurs qui concernent les types de contrats ou les privilèges utilisateur. Si vous décidez de mettre en œuvre ce projet dans son intégralité, il vous faudra sans doute procéder à des adaptations selon votre propre contexte professionnel.

IV-A. La structure des tables de données fixes

IV-A-1. tbl_ListeDepartements (les départements*)

Cette table possède trois champs :

  • le champ IDDepartement de type Texte limité à trois caractères ;
  • le champ Departements de type Texte limité à soixante-quatre caractères ;
  • le champ IDRegion de type numérique Entier long .

La clé primaire est affectée au champ IDDepartement.

Image non disponible

IV-A-2. tbl_ListeDesCommunes (Les communes*)

Cette table possède cinq champs :

  • le champ IDCommune représenté par un identifiant de type Numérotation automatique ;
  • le champ CodeINSEE de type Texte limité à cinq caractères ;
  • le champ NomCommune de type Texte limité à soixante-quatre caractères ;
  • le champ CodePostal de type Texte limité à cinq caractères ;
  • le champ IDDepartement de type Texte limité à trois caractères .

La clé primaire est affectée aux champs CodeINSEE + NomCommune (CodeINSEE indexé sans doublons).

Image non disponible

IV-A-3. tbl_ListeDesRegions (les régions*)

Cette table possède deux champs :

  • le champ IDRegion de type Numérotation automatique ;
  • le champ Region de type Texte limité à soixante-quatre caractères.

La clé primaire est affectée au champ IDRegion.

Image non disponible

Du fait que ces tables (*) sont facultatives, dans la mesure où elles ne sont utilisées dans ce projet qu'à titre d'exemple, vous n'êtes pas obligé de les mettre en œuvre…

IV-A-4. tbl_Privileges (les privilèges utilisateur**)

Cette table possède trois champs :

  • le champ IDPrivilege de type Numérique Entier long ;
  • le champ CodePrivilege de type Texte limité à cinq caractères ;
  • le champ LibellePrivilege de Texte limité à soixante-quatre caractères.

La clé primaire est affectée aux champs IDPrivilege + CodePrivilege tous indexés sans doublon.

Image non disponible

IV-A-5. tbl_TypesContrat (les types de contrats**)

Cette table possède deux champs :

  • le champ IDTypeContrat de type Numérique Entier long ;
  • le champ TypeContrat de type Texte limité à soixante-quatre caractères.

La clé primaire est affectée au champ IDTypeContrat indexé sans doublon.

Image non disponible

IV-B. La structure des tables de données utilisateur

IV-B-1. tblCommunesSite (les associations communes et site)

Cette table possède trois champs :

  • le champ IDCommune de type Numérique Entier long ;
  • le champ NoSite de type Numérique Entier long ;
  • le champ IDDepartement de type Texte limité à trois caractères.

La clé primaire est affectée aux champs IDCommune + NoSite tous indexés avec doublons.

Image non disponible

IV-B-2. tblContratCommunes (les associations contrat et communes)

Cette table possède deux champs :

  • le champ IDCommune de type Numérique Entier long ;
  • le champ IDContrat de type Texte limité à cinq caractères.

La clé primaire est affectée aux champs IDCommune + IDContrat tous indexés avec doublons.

Image non disponible

IV-B-3. tblContrats (Les contrats)

Cette table possède cinq champs :

  • le champ IDCommune représenté par un identifiant de type Numérotation automatique ;
  • le champ IDContrat de type Texte limité à cinq caractères ;
  • le champ LibelleContrat de type Texte limité à 128 caractères ;
  • le champ DateFin de type Date ;
  • le champ IDTypeContrat de type Numérique Entier long.

La clé primaire est affectée au champ IDContrat indexé sans doublon.

Image non disponible

IV-B-4. tblPersonnel (Le personnel)

Cette table possède six champs :

  • le champ IDPersonne représenté par un identifiant de type Numérotation automatique ;
  • le champ NomFamille Texte limité à soixante-quatre caractères ;
  • le champ Prenom de type Texte limité à soixante-quatre caractères ;
  • le champ EMAIL de type Texte limité à soixante-quatre caractères ;
  • le champ Login de type Texte limité à soixante-quatre caractères ;
  • le champ NoSite de type Numérique Entier long ;
  • le champ IDPrivileges de type Numérique Entier long.

La clé primaire est affectée aux champs NomFamille + Prenom + NoSite tous indexés avec doublons.

Image non disponible

IV-B-5. tblSites (Les sites)

Cette table possède quatre champs :

  • le champ NoSite de type Numérique Entier long ;
  • le champ LibelleSite de type Texte limité à soixante-quatre caractères ;
  • le champ IDCommune de type Numérique Entier long ;
  • le champ DateOuverture de type Date.

La clé primaire est affectée aux champs NoSite + LibelleSite

NoSite est indexé sans doublon et LibelleSite est indexé avec doublons.

Image non disponible

IV-C. Données des tables

Pour simplifier les choses pour le développeur, j'ai adopté intentionnellement une convention de nommage pour les tables qui contiennent des données prédéfinies avec un caractère underscore (_).

Rappel
Celles qui possèdent un astérisque(*) sont des tables facultatives puisqu'elles ne contiennent que les régions, les départements et les communes, et concernent plus particulièrement ce projet.
Celles qui possèdent deux astérisques(**) sont des tables directement liées au projet si l'on s'en réfère à l'aspect métier de ce dernier.

Toutes les autres tables sont dédiées à recevoir des données saisies par les utilisateurs. Ce seront ces tables qui seront remises à blanc, si toutefois l'utilisateur en fait la demande au chargement du projet. Nous verrons cela un petit peu plus bas dans le processus d'utilisation des formulaires.

Exemple de données dans les tables

IV-C-1. Liste des départements (tbl_ListeDepartements)

IDDepartement

Departement

IDRegion

01

Ain

23

02

Aisne

20

03

Allier

3

04

Alpes-de-Haute-Provence

22

05

Hautes-Alpes

22

06

Alpes-Maritimes

22

IV-C-2. Liste des régions (tbl_ListeDesRegions)

IDRegion

Region

1

Alsace

2

Aquitaine

3

Auvergne

4

Basse-Normandie

5

Bourgogne

Liste des communes (tbl_ListeDesCommunes)

IDCommune

Code INSEE

Commune

Code Postal

IDDepartement

447

98611

ALO

98610

98

603

98711

ANAA

98760

98

1307

98712

ARUE

98701

98

1308

98713

ARUTUA

98761

98

3007

98801

BELEP

98811

98

4276

98714

BORA BORA

98730

98

4510

98802

BOULOUPARI

98812

98

4524

98803

BOURAIL

98870

98

5855

98804

CANALA

98813

98

10485

98805

DUMBEA

98837

98

11481

98715

FAAA

98704

98

11508

98716

FAKARAVA

98790

98

IV-C-3. Liste des types de contrats (tbl_TypesContrat)

IDTypeContrat

Type de contrat

1

Contrat communal

2

Contrat intercommunal

3

Contrat fictif

IV-C-4. tbl_Privileges

IDPrivilege

Code Privilège

Libellé Privilèges

1

ADMR

Administrateur Régional

2

ADMS

Administrateur du Site

3

USERA

Utilisateur Agence

4

GUEST

Invité ou Externe

V. Le schéma relationnel de l'application

Bien qu'il ne soit pas obligatoire d'en élaborer un, le schéma relationnel a été mis en place dans ce projet. Il est représenté par l'illustration ci-dessous :

Image non disponible

Cela permet, en tout cas ici, de mieux appréhender la structure…

Vous pouvez remarquer qu'il y a deux jeux de tables distincts :

  • un jeu de tables concernant les données utilisateur ;
  • un jeu de tables concernant uniquement les communes, leur département et leur région.

VI. Conception des formulaires

Il existe autant de formulaires qu'il y a d'étapes.

Dans l'exemple présenté ici, je me suis limité à cinq écrans qualifiés à proprement parler de saisies, auxquels j'ai ajouté un formulaire d'accueil et un formulaire final récapitulatif.

Ce projet comporte donc onze formulaires :

  • le premier et le dernier sont les formulaires de début et de fin ;
  • trois d'entre eux possèdent un sous-formulaire ;
  • un autre est un sous-formulaire de type pop-up ;
  • deux autres sont des formulaires simples.

VI-A. Les différents formulaires (en mode utilisation)

Dans cet assistant, parmi les cinq formulaires de saisie, se trouvent des formulaires dits « simples ». Les autres seront élaborés avec une structure Parent/Enfant, autrement dit, un formulaire principal et un sous-formulaire, ce dernier possédant une structure continue.

VI-A-1. Le formulaire d'accueil

Image non disponible

VI-A-2. Le formulaire de saisie du site

Image non disponible

VI-A-3. Le formulaire de saisie du personnel

Image non disponible

VI-A-4. Le formulaire de saisie des communes

Image non disponible

VI-A-5. Le formulaire de saisie des contrats

Image non disponible

VI-A-6. Le formulaire de saisie de l'association Contrat-Communes

Image non disponible

VI-A-7. Le formulaire de fin

Image non disponible

VII. Description structurelle des formulaires

VII-A. La source de données formulaires

Tous les formulaires dédiés à la saisie se voient affecter leur source de données dynamiquement par code. Il est rappelé ici que l'objectif de ce tutoriel est de vous apprendre à minimiser l'écriture du code d'une part, et de faire en sorte que chaque feuille de code représentée par la classe des formulaires soit la plus générique possible.

Considérant que pratiquement tous les écrans restent identiques selon leur type (simple ou continu), il était important de faire en sorte que la source de données bénéficie des mêmes privilèges.

Vue éclatée des formulaires (description des contrôles)

Pour faciliter la mise en œuvre des formulaires, les paragraphes qui vont suivre vous présentent une photographie du formulaire en Mode création tel qu'il est vu à l'écran lorsqu'il s'agit d'un formulaire simple, et en vue éclatée lorsqu'il s'agit d'un sous-formulaire.

En effet, du fait que certains contrôles sont volontairement cachés en mode formulaire, il est plus aisé de se rendre compte de la façon dont l'ensemble des contrôles a été agencé et disposé.

VII-B. Les formulaires simples

Ils sont au nombre de quatre :

deux formulaires sans données saisies et deux avec données saisies par l'utilisateur.

VII-B-1. Formulaire sans données utilisateur

Ces formulaires ne sont pas structurés de la même façon.

Ils sont essentiellement composés de contrôles étiquette informative et de boutons de navigation.

Ces deux formulaires sont :

  • le formulaire d'accueil : frmStart ;
  • le formulaire de fin : frmEnd.

VII-B-2. Formulaire avec données utilisateur

Ces formulaires sont tous structurés de la même façon.

Ils possèdent tous un contrôle étiquette permettant d'affecter dynamiquement le contenu du titre qui est alimenté directement au moment du chargement.

Juste au-dessous de celui-ci se trouvent les contrôles des champs de la source.

Dans la partie basse de chacun d'eux, se trouvent quatre boutons de commande :

  • un bouton Supprimer qui recevra la légende Sauvegarder en cours de saisie ;
  • un bouton Ajouter qui recevra la légende Annuler en cours de saisie ;
  • un bouton intitulé Précédent qui permet de revenir à l'écran précédent ;
  • et un bouton Suivant qui permet de passer à l'écran suivant.

Au niveau des propriétés

Vous définirez les propriétés ci-après comme suit :

  • Cycle à « Enregistrement en cours » ; cette propriété permet de spécifier ce qui se produit lorsque vous appuyez sur la touche TAB pendant que le dernier contrôle d'un formulaire dépendant est activé ;
  • Menu Contextuel à « Non » ; cette propriété permet de spécifier s'il doit être affiché lorsque vous cliquez sur un objet d'un formulaire à l'aide du bouton droit de la souris ;
  • Type Recordset à « Feuille de réponse dynamique »  pour permettre de spécifier quel genre de jeu d'enregistrements est disponible pour le formulaire ;
  • Propriété Remarque (Tag) définie à Null ou NotNull selon les champs pour lesquels vous souhaitez ne pas considérer la nullité comme bloquante.

Explications
Tous les champs des formulaires simples voient leur propriété Tag définie à Null ou NotNull  :

cette propriété est utilisée dans une fonction qui contrôle le bon contenu des champs.

Voir cette rubrique pour plus d'informations ici.

VII-C. Les formulaires avec un sous-formulaire

VII-C-1. Les formulaires parents

Ils sont au nombre de trois…

Ces formulaires sont tous structurés de la même façon.

Ils possèdent tous un contrôle étiquette permettant d'affecter dynamiquement le titre, le sujet et juste au-dessous de ceux-ci, se trouvent des zones de texte cachées que j'ai intentionnellement coloriées en jaune avec une police de couleur rouge.

Attention

Ces champs voient leur propriété Visible dépendante de la constante SHOW_HIDDEN_CONTROLS.

En leur milieu se trouve un sous-formulaire pour lequel il n'y a volontairement pas de notion Champs pères et Champs fils.

Les sous-formulaires portent toujours le même nom (sfrmDataWizard), et ce, quel que soit leur formulaire Parent.

En dessous de leur sous-formulaire se trouvent deux contrôles étiquette, l'un spécifiant le nombre d'enregistrements inscrits dans le sous-formulaire, l'autre spécifiant par effet de clignotement que la ligne est complète ou incomplète dans le sous-formulaire.

Cette dernière étiquette clignote grâce à l'événement Timer.

Dans la partie basse de chacun des formulaires parents sont dessinés quatre boutons de commande :

  • un bouton Supprimer qui recevra la légende Sauvegarder en cours de saisie ;
  • un bouton Ajouter qui recevra la légende Annuler en cours de saisie ;
  • un bouton Précédent qui permet de revenir à l'écran précédent ;
  • et un bouton Suivant qui permet de passer à l'écran suivant.

VII-C-2. Les formulaires enfants (ou sous-formulaire)

Chacun des sous-formulaires possède dans la partie droite de sa zone de détail, une case à cocher dont la formule calcule en permanence (donc au moment de la saisie) le fait que la ligne est complétée ou non.

On considère que la ligne est complétée à partir du moment où la formule stockée dans le champ caché (de couleur jaune) nommé txtCheckdata renvoie bien le résultat attendu.

Ce champ possède une formule appellant une fonction écrite en VBA qui calcule la somme de la nullité des champs de données avec leur type associé via la fonction Nz().

La fonction en elle-même retourne 0 ou 1 selon que le champ est nul ou non, et ce, quel que soit son type. Selon le nombre de champs où la valeur est requise, une certaine somme est attendue.

Ainsi, pour la table du personnel du site, l'ensemble des six champs est obligatoire ; cela implique que la formule doit renvoyer tout simplement 6.

La case à cocher prendra la valeur True ou False selon ce résultat.

Exemple de formule pour un sous-formulaire :

la zone de texte cachée « txtCheckdata »

la zone de texte cachée « txtCheckdata »
Sélectionnez
=(NullData([NomDuChamp1];4)+NullData([NomDuChamp2];10)+NullData([NomDuChamp3];10))

et la case à cocher associée « chkCompleted »

la case à cocher « chkCompleted »
Sélectionnez
=([txtCheckdata]=3)

la fonction NullData() sera expliquée un peu plus loin dans ce tutoriel.

Dans la partie basse de chacun des sous-formulaires, au niveau du pied de formulaire, se trouvent des zones de texte cachées dont deux d'entre elles calculent en permanence le nombre d'enregistrements pour l'une et la valeur de la case à cocher pour l'autre.

Les autres zones de texte cachées reprennent selon la source de données, les champs utilisés pour accéder à l'enregistrement, et donc leur identifiant d'une part et leur libellé d'autre part ; les valeurs de ces champs sont utilisées au niveau du formulaire parent pour pouvoir afficher le contenu dans un message, notamment lorsqu'il s'agit de supprimer la ligne.

Remarque

Chacune des zones de texte cachées situées dans le pied de formulaire a une propriété Visible définie à False et une hauteur fixée à 0.

VII-C-3. Au niveau des propriétés

Vous définirez les propriétés ci-après comme suit :

  • Cycle à « Tous les enregistrements » ; cette propriété permet de spécifier ce qui se produit lorsque vous appuyez sur la touche TAB pendant que le dernier contrôle d'un formulaire dépendant est activé ;
  • Menu Contextuel à « Non » ; cette propriété permet de spécifier s'il doit être affiché lorsque vous cliquez sur un objet d'un formulaire à l'aide du bouton droit de la souris ;
  • Type Recordset à « Feuille de réponse dynamique » pour permettre de spécifier quel genre de jeu d'enregistrements est disponible pour le formulaire.

VIII. Description détaillée des formulaires

VIII-A. Le formulaire d'accueil

Ce formulaire se nomme frmStart.

Ce formulaire dépourvu de données est composé principalement de contrôles étiquette dédiés à présenter quelque peu l'interface, mais aussi :

  • d'une image représentant un logo par exemple ;
  • d'une case à cocher permettant de remettre à blanc les données ;
  • et de boutons de commande, un pour quitter, l'autre pour démarrer.

Le titre de ce formulaire est statique du fait que c'est le formulaire d'accueil.

Image non disponible

VIII-B. Le formulaire de saisie du site

Ce formulaire se nomme frmDataWizard1.

Il s'agit du formulaire qui contient les coordonnées du site.

Ce formulaire possède :

  • un contrôle de type étiquette présentant succinctement l'objet du formulaire ;
  • trois zones de texte et deux listes déroulantes ;
  • et trois boutons de commande.
Image non disponible

La première zone de texte définira le numéro du site, la deuxième, le libellé et la troisième, la date d'ouverture du site.

La zone de liste déroulante indépendante permet de lister les départements et leur région et possède le code événementiel associé pour pouvoir filtrer les communes correspondant à celui sélectionné.

La zone de liste des communes contient l'ensemble des communes issues de la table via une instruction SQL filtrée sur le critère précité.

Dans la partie basse du formulaire se trouvent trois boutons :

  • un bouton intitulé Effacer qui permet de remettre à blanc la saisie en cours ;
  • un bouton intitulé Précédent qui permet de revenir à l'écran précédent ;
  • et un bouton Suivant qui permet de passer à l'écran suivant.

VIII-C. Le formulaire de saisie du personnel

Ce formulaire est nommé : frmDataWizard2.

Il s'agit du formulaire permettant de saisir les employés du site.

Image non disponible

La partie haute du formulaire contient les cinq champs cachés qui récupèrent les valeurs du pied de formulaire du sous-formulaire.

Juste en dessous du sous-formulaire, se trouvent les deux étiquettes qui calculent par programme le nombre d'employés enregistrés d'une part et si la ligne en cours de saisie dans le sous-formulaire est complète d'autre part.

Champ

Formule ou Source

Description

Image non disponible

txtTotalRows

=[frmDataWizard2_sub]![txtCountRows]

Valeur de txtCountRows*

Image non disponible

txtNoSite

=[frmDataWizard2_sub]![txtNoSite]

Valeur de txtNoSite*

Image non disponible

txtIDPersonne

=[frmDataWizard2_sub]![txtIDPersonne]

Valeur de txtIDPersonne*

Image non disponible

txtNomPrenom

=[frmDataWizard2_sub]![txtNomPrenom]

Valeur de txtNomPrenom*

Image non disponible

txtRowCompleted

=[frmDataWizard2_sub]![txtCompleted]

Valeur de txtCompleted*

* issues des champs du sous-formulaire de la zone pied de formulaire (les champs portent le même nom pour plus de commodité).

VIII-C-1. Le sous-formulaire de saisie du personnel

Ce formulaire est nommé : frmDataWizard2_sub.

Il possède tous les champs de la table du personnel (tblPersonnel).

Image non disponible

Dans la partie En-tête sont posées des étiquettes correspondant à chacun des champs de la table et dans la partie Détail, les champs eux-mêmes.

Dans la zone de détail se trouvent les champs cachés :

  • la zone de texte « txtCheckdata » dont la formule associée est :

    Formule de txtCheckdata 
    Sélectionnez
    =(NullData([NoSite];4)+NullData([Prenom];10)+NullData([NomFamille];10)+NullData([EMAIL];10)+NullData([LOGIN];10)+NullData([IDPrivileges];4))
  • la case à cocher « chkCompleted » dont la formule associée est :
Formule de chkCompleted 
Sélectionnez
=([txtCheckdata]=6)

Dans le pied du formulaire se trouvent les champs cachés dont les formules permettent de comptabiliser le nombre d'employés, de renvoyer l'identifiant, le libellé (ici nom et prénom) et la complétion de l'enregistrement recevant le focus (donc en cours).

Champ

Formule ou Source

Description

Image non disponible

txtCheckdata

=NullData(de chaque champ)

Calcule la somme des Nz()+Nz()+…

Image non disponible

txtCountRows

=Compte([IDPersonne])

Compte le nombre d'enregistrements

Image non disponible

txtNoSite

NoSite

Renvoie le numéro du site en cours

Image non disponible

txtIDPersonne

IDPersonne

Renvoie l'ID de l'employé en cours

Image non disponible

txtNomPrenom

=[Prenom] & " " & [NomFamille]

Renvoie le nom et le prénom en cours

Image non disponible

txtCompleted

=[chkCompleted]

Renvoie la valeur de la case à cocher

VIII-D. Le formulaire de saisie des communes

Ce formulaire est nommé : frmDataWizard3.

Il s'agit du formulaire permettant de sélectionner les communes exploitées par le site.

Image non disponible

À l'instar du formulaire de saisie des employés, celui-ci possède quatre champs cachés dont les formules restent identiques à ceci près qu'elles ne pointent pas vers les mêmes champs, excepté bien entendu celle qui compte le nombre de lignes et celle qui retourne la complétion de l'enregistrement en cours.

Champ

Formule ou Source

Description

Image non disponible

txtTotalRows

=[frmDataWizard3_sub]![txtCountRows]

Valeur de txtCountRows*

Image non disponible

txtIDCommune

=[frmPerimeter3_sub]![txtIDCommune]

Valeur de txtIDCommune*

Image non disponible

txtCommune

=[frmPerimeter3_sub]![txtCommune]

Valeur de txtCommune*

Image non disponible

txtRowCompleted

=[frmDataWizard3_sub]![txtCompleted]

Valeur de txtCompleted*

* issues des champs du sous-formulaire de la zone pied de formulaire (les champs portent le même nom pour plus de commodité).

VIII-D-1. Le sous-formulaire de saisie des communes

Ce formulaire est nommé : frmDataWizard3_sub.

Il possède tous les champs de la table du personnel (tblCommunesSite).

Image non disponible

Dans la partie En-tête seront posées des étiquettes correspondant à chacun des champs de la table et dans la partie Détail, les champs eux-mêmes.

Dans la zone de détail se trouvent les champs cachés :

  • la zone de texte « txtCheckdata » dont la formule associée est :

    Formule de txtCheckdata
    Sélectionnez
    =(NullData([NoSite];4)+NullData([IDCommune];4))
  • la case à cocher « chkCompleted » dont la formule associée est :
Formule de chkCompleted 
Sélectionnez
=([txtCheckdata]=2)

Dans le pied du formulaire se trouvent les champs cachés dont les formules permettent de comptabiliser le nombre de communes, de renvoyer l'identifiant, le libellé (ici nom de la commune) et la complétion de l'enregistrement recevant le focus (donc en cours).

Champ

Formule ou Source

Description

Image non disponible

txtCheckdata

=NullData(de chaque champ)

Calcule la somme des Nz()+Nz()+…

Image non disponible

txtCountRows

=Compte([IDPersonne])

Compte le nombre d'enregistrements

Image non disponible

txtIDCommune

IDCommune

Renvoie l'ID de la commune en cours

Image non disponible

txtCommune

=[IDCommune].[column](1)

Valeur de txtNomCommune

Image non disponible

txtCompleted

=[chkCompleted]

Valeur de la case à cocher

VIII-E. Le formulaire de saisie des contrats

Ce formulaire est nommé : frmDataWizard4_Popup.

Il s'agit du formulaire qui contient les différents contrats.

Image non disponible

Ce formulaire possède :

  • quatre zones de texte et une liste déroulante pour les données propres au contrat ;
  • cinq boutons de commande.
    La première zone de texte définira le numéro du contrat, la seconde, son libellé, et les deux dernières, respectivement la date de début et la date de fin du contrat.
    La zone de liste déroulante permet de lister les types de contrats.
    Dans la partie basse du formulaire se trouvent les cinq boutons :
  • un bouton intitulé Supprimer qui permet de remettre à blanc la saisie en cours ;
  • un bouton intitulé Ajouter qui permet d'ajouter un nouveau contrat ;
  • un bouton intitulé Voir les contrats déjà saisis qui permet de visualiser tous les contrats déjà enregistrés ;
  • un bouton intitulé Précédent qui permet de revenir à l'écran précédent ;
  • et un bouton Suivant qui permet de passer à l'écran suivant.

Image non disponible

Le champ IDTypeContrat représenté par une Zone de liste déroulante est attaché à la table tbl_TypesContrat à l'aide d'une requête :

Requête qry_CBOListeTypesDeContrat
Sélectionnez
SELECT tbl_TypesContrat.IDTypeContrat, tbl_TypesContrat.TypeContrat
FROM tbl_TypesContrat;

Pour plus de commodité, j'ai stocké cette clause SQL dans une requête nommée :

qry_CBOListeTypesDeContrat.

VIII-E-1. Le formulaire « popup » des contrats

Ce formulaire est nommé : frmDataWizard4_Popup

Il s'agit du formulaire qui contient l'ensemble des contrats que vous avez déjà saisis.

Image non disponible

Il est à noter que le bouton d'accès à ce formulaire reste grisé tant que le nombre de contrats est inférieur ou égal à un.

Ce formulaire est construit en mode continu avec une structure En-tête et Pied de formulaire.

  • Dans la partie En-tête seront posées des étiquettes correspondant à chacun des champs de la table et dans la partie Détail, les champs eux-mêmes.
  • Dans le Pied du formulaire se trouvent :
  1. Un champ avec une formule qui permet de comptabiliser le nombre de contrats existants ;
  2. Et sur la droite, un bouton permettant de refermer la fenêtre.

Dans ce formulaire, tous les contrôles sont verrouillés là où la propriété Arrêt tabulation est définie à False.

En haut à gauche et de façon invisible, se trouve un champ nommé txtFocus et qui reçoit le focus en permanence.

La formule du champ de comptabilisation est la suivante :

Formule du champ txtNombreContrat
Sélectionnez
=Compte([IDContrat]) & " contrats enregistrés."

VIII-F. Le formulaire de saisie de l'association Contrat-Communes

Ce formulaire est nommé : frmDataWizard5.

Il s'agit du formulaire permettant de sélectionner les communes exploitées par le site.

Image non disponible

À l'instar du formulaire de saisie des employés, celui-ci possède quatre champs cachés dont les formules restent identiques à ceci près qu'elles ne pointent pas vers les mêmes champs, excepté bien entendu celle qui compte le nombre de lignes et celle qui retourne la complétion de l'enregistrement en cours.

Champ

Formule ou Source

Description

Image non disponible

txtTotalRows

=[frmDataWizard5_sub]![txtCountRows]

Valeur de txtCountRows*

Image non disponible

txtIDContrat

=[frmPerimeter5_sub]![IDContrat]

Valeur de txtIDContrat*

Image non disponible

txtIDCommune

=[frmPerimeter5_sub]![txtIDCommune]

Valeur de txtIDCommune*

Image non disponible

txtNomCommune

=[frmPerimeter5_sub]![txtNomCommune]

Valeur de txtNomCommune*

Image non disponible

txtRowCompleted

=[frmDataWizard5_sub]![txtCompleted]

Valeur de txtCompleted*

* issues des champs du sous-formulaire de la zone pied de formulaire (les champs portent le même nom pour plus de commodité).

VIII-F-1. Le sous-formulaire de saisie de l'association Contrat-Communes

Ce formulaire est nommé : frmDataWizard5_sub.

Ce formulaire possède tous les champs de la table du personnel (tblContratCommunes).

Image non disponible

Dans la partie En-tête seront posées des étiquettes correspondant à chacun des champs de la table et dans la partie Détail, les champs eux-mêmes.

Dans la zone de détail se trouvent les champs cachés :

  • la zone de texte « txtCheckdata » dont la formule associée est :

    Formule de txtCheckdata
    Sélectionnez
    =(NullData([IDContrat];10)+NullData([IDCommune];4))
  • la case à cocher « chkCompleted » dont la formule associée est :
Formule de chkCompleted
Sélectionnez
=([txtCheckdata]=2)

Dans le pied du formulaire se trouvent les champs cachés dont les formules permettent de comptabiliser le nombre d'employés, de renvoyer l'identifiant, le libellé (ici nom de la commune et N° du contrat) et la complétion de l'enregistrement recevant le focus (donc en cours).

Champ

Formule ou Source

Description

Image non disponible

txtCheckdata

=NullData(de chaque champ)

Calcule la somme des Nz()+Nz()+…

Image non disponible

txtCountRows

=Compte([IDContrat])

Compte le nombre d'enregistrements

Image non disponible

txtIDContrat

IDContrat

Renvoie l'ID du contrat en cours

Image non disponible

txtIDCommune

IDCommune

Renvoie l'ID de la commune en cours

Image non disponible

txtNomCommune

=[IDCommune].[column](1)

Renvoie le nom de la commune en cours

Image non disponible

txtCompleted

=[chkCompleted]

Renvoie la valeur de la case à cocher

VIII-G. Le formulaire de fin

C'est le dernier formulaire qui s'affiche pour résumer les étapes omises (s'il y en a) et pour clore l'application après confirmation.

Le projet a été élaboré pour être ouvert autant de fois que nécessaire de manière à ce que l'enrichissement des informations puisse se faire sur plusieurs jours.

Ce formulaire, tout comme le formulaire d'accueil, est composé essentiellement de contrôles type étiquette et trois boutons de commande.

Image non disponible

Deux de ces étiquettes sont alimentées par code, les autres possèdent un texte statique.

Dans la partie basse du formulaire se trouvent les trois boutons :

  • un bouton intitulé Début qui permet de revenir au début des étapes ;
  • un bouton intitulé Précédent qui permet de revenir à l'écran précédent ;
  • et un bouton Terminer qui permet de fermer l'application après confirmation.

Contrôle

Source

Description

Image non disponible

lblStepStatus

Code

Affiche la mention de complétion selon les étapes

Image non disponible

lblStepList

Code

Affiche la liste des étapes qui ont été omises

IX. Démarrage du projet

IX-A. Chargement par macro exemple 1

Dans le cas présenté ici, le projet démarre à l'aide d'une macro AutoExec qui pilote l'ouverture du formulaire d'accueil via l'action OuvrirFormulaire, le formulaire étant frmStart (formulaire d'accueil) et le mode de fenêtre étant Boîte de dialogue.

IX-B. Chargement par macro exemple 2

Autre possibilité, vous pouvez encore appeler une fonction personnalisée via une macro AutoExec qui ouvrira davantage de possibilités qu'offrent les actions des macros. Vous utiliserez alors la méthode OpenForm de l'objet DoCmd avec le paramètre acDialog.

IX-C. Chargement par propriété intrinsèque de la base de données

Autre solution, vous pouvez tout aussi bien définir les propriétés de démarrage de la base de données et sélectionner ce même formulaire.

X. Description des événements au sein des formulaires

Nous allons décrire ici les procédures événementielles qui se trouvent au sein des différentes classes de formulaire.

Pour chaque feuille, l'Option Explicit a été affectée et ne doit pas être supprimée.

Cette option se définit d'elle-même si vous cochez la case de l'éditeur VBE.

Image non disponible

Tous les formulaires des étapes (ceux où l'utilisateur saisit des données) possèdent à peu de choses près, le même jeu de constantes dans l'en-tête :

Constante

Description

PREVIOUS_FORM_ID

Index du formulaire précédent

NEXT_FORM_ID

Index du formulaire suivant

TARGET_TABLE

Nom de la table source

FIELD_COLLECTION

Chaîne de tableau contenant la liste des champs utilisés

RECORD_SOURCE

Clause SELECT basée sur TARGET_TABLE

DELETE_RECORD_SQL

Clause DELETE basée sur TARGET_TABLE

WHERE_CONDITION

Clause WHERE de la clause DELETE_RECORD_SQL

FORM_CAPTION

Légende de la barre de titre du formulaire

MAX_STEP_WIZ

Constante globale (autre module) définissant le nombre d'étapes

FORM_SUBJECT

Sujet du formulaire (mot clé utilisé dans le code)

FORM_TITLE

Titre du formulaire situé en haut de celui-ci

XI. Les procédures événementielles des formulaires

XI-A. Le formulaire d'accueil (frmStart)

XI-A-1. L'en-tête de la feuille

L'en-tête ici comporte l'index du formulaire suivant et le titre de la fenêtre dans deux constantes dûment nommées.

frmStart
Sélectionnez
Option Compare Database
Option Explicit

Private Const NEXT_FORM_ID                             As Integer = 1
Private Const FORM_CAPTION                             As String = "Écran d'accueil"

XI-A-2. Les événements de formulaire

La procédure SurChargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On désactive les alertes système
    DoCmd.SetWarnings False
    'On réinitialise les variables et les contrôles
    Me.chkRAZData = False
    g_strDataPage = ""
    'On efface le tableau des étapes
    Erase g_intCompletionIndex
    Me.Caption = FORM_CAPTION
End Sub

On désactive les alertes système.

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

On force la valeur False à la case à cocher et on attribue une valeur vide à g_strDataPage.

Quelques explications…

  • Le contrôle case à cocher chkRAZData (Langue que vous reconnaissez grâce à son préfixe) est mis à False.
  • Le tableau d'entiers nommé g_intCompletionIndex est vidé ; c'est lui qui contient la liste des étapes écran par écran et affecte une valeur True ou False à chacun des éléments selon que le formulaire est complété ou non.

Il n'y a pas d'autre événement pour ce formulaire.

XI-B. Le formulaire de saisie du site (frmDataWizard1)

XI-B-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard1
Sélectionnez
Option Compare Database
Option Explicit

Private Const PREVIOUS_FORM_ID                         As Integer = 0
Private Const NEXT_FORM_ID                             As Integer = 2
Private Const TARGET_TABLE                             As String = "tblSites"
Private Const FIELD_COLLECTION                         As String = "NoSite;LibelleSite;IDCommune;DateOuverture"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE
Private Const DELETE_RECORD_SQL                        As String = "DELETE * FROM " & TARGET_TABLE & " "
Private Const WHERE_CONDITION                          As String = "WHERE NoSite = #1"
Private Const FORM_CAPTION                             As String = "Saisie des données : étape #1/" & MAX_STEP_WIZ

Private Const FORM_SUBJECT                             As String = "du Site"
Private Const FORM_TITLE As String = " Assistant de saisie des données " & FORM_SUBJECT

Vous reconnaîtrez parmi elles, les index des formulaires suivant et précédent, le titre et le sujet de la fenêtre. Les autres constantes concernent la navigation, la source de données et les différentes clauses SQL.

XI-B-2. Les événements de formulaire

La procédure SurChargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'Le formulaire est raccordé à la source de données de la constante
    Me.RecordSource = RECORD_SOURCE
    'On applique le titre de la barre de titre dynamiquement
    Me.Caption = Replace(FORM_CAPTION, "#1", NEXT_FORM_ID - 1)
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
    'On appelle InitIDCommune()
    Call InitIDCommune
End Sub

Quelques explications…

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

La fonction InitIDCommune() remplit la zone de liste indépendante en fonction d'une commune qui aurait préalablement été renseignée (cas de navigation en allers et retours).

La fonction privée se trouve ici : InitIDCommune.

La procédure SurErreur()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim strErrMessage                                      As String
    'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    If FormDataErrors(DataErr, strErrMessage, Response, Me) Then
        MsgBox strErrMessage, vbExclamation, "Erreur de saisie"
        Me.ActiveControl.Undo
    End If
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

Il n'y a pas d'autre événement pour ce formulaire.

XI-C. Le formulaire de saisie du personnel (frmDataWizard2)

XI-C-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard2
Sélectionnez
Option Compare Database
Option Explicit

Private Const PREVIOUS_FORM_ID                         As Integer = 1
Private Const NEXT_FORM_ID                             As Integer = 3
Private Const TARGET_TABLE                             As String = "tblPersonnel"
Private Const FIELD_COLLECTION                         As String = "NoSite;Prenom;NomFamille;Email;Login;IDPrivileges"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"
Private Const DELETE_RECORD_SQL                        As String = "DELETE * FROM " & TARGET_TABLE & " "
Private Const WHERE_CONDITION                          As String = "WHERE IDPersonne = #1 AND NoSite = '#2'"
Private Const FORM_CAPTION                             As String = "Saisie des données : étape #1/" & MAX_STEP_WIZ

Private Const FORM_SUBJECT                             As String = "des employés rattachés au Site"
Private Const FORM_TITLE As String = " Assistant de saisie des données " & FORM_SUBJECT

XI-C-2. Les événements de formulaire

La procédure Sur_Activation()

Elle est invoquée et se traduit par le code suivant :

Form_Current
Sélectionnez
Private Sub Form_Current()
'On appelle la procédure événementielle générique du même nom
    Call FormCurrent(Me, "Prenom")
End Sub

Quelques explications…

On appelle la procédure générique FormCurrent avec le paramètre Me pour identifier le formulaire en cours et le nom du contrôle devant être sollicité dans celle-ci.

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim strErrMessage                                      As String
    'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    If FormDataErrors(DataErr, strErrMessage, Response, Me) Then
        MsgBox strErrMessage, vbExclamation, "Erreur de saisie"
        Me.ActiveControl.Undo
    End If
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement ()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On désactive les alertes
    DoCmd.SetWarnings False
    'On masque ou affiche les contrôles selon le mode (Prod ou Test)
    Me.txtTotalRows.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtNoSite.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtIDPersonne.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtNomPrenom.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtTotalRows.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtRowCompleted.Visible = SHOW_HIDDEN_CONTROLS

    'Le formulaire est raccordé à la source de données de la constante
    Me.RecordSource = RECORD_SOURCE
    'On applique le titre de la barre de titre dynamiquement
    Me.Caption = Replace(FORM_CAPTION, "#1", NEXT_FORM_ID - 1)
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
End Sub

Quelques explications…

On affecte la visibilité des contrôles de sous-totaux via la constante SHOW_HIDDEN_CONTROLS.

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

La procédure Sur_Minuterie()

Elle est invoquée et se traduit par le code suivant :

Form_Timer
Sélectionnez
Private Sub Form_Timer()
    'On provoque un clignotement du texte de complétion des données
    Me.lblRecCompleted.Visible = Not lblRecCompleted.Visible
End Sub

Cet événement provoque ici le clignotement du texte de complétion des données selon la valeur du champ situé dans le sous-formulaire.

Il n'y a pas d'autre événement pour ce formulaire.

XI-D. Le sous-formulaire de saisie des communes (frmDataWizard2_sub)

XI-D-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard2_sub
Sélectionnez
Option Compare Database
Option Explicit

Private Const TARGET_TABLE                             As String = "tblPersonnel"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"

XI-D-2. Les événements de formulaire

La procédure Sur_Activation()

Elle est invoquée et se traduit par le code suivant :

Form_Current
Sélectionnez
Private Sub Form_Current()
'On appelle la procédure événementielle générique du même nom
    Call SubFormCurrent(Me, "employé", False)
End Sub

Quelques explications…

On appelle la procédure générique FormCurrent avec le paramètre Me pour identifier le formulaire en cours et le nom du contrôle devant être sollicité dans celle-ci.

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    SubFormDataError Me, DataErr, Response, "même nom / même prénom / même  de site"
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
    'On affecte la source de données
    Me.RecordSource = RECORD_SOURCE
End Sub

Quelques explications…

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

Il n'y a pas d'autre événement pour ce formulaire.

XI-E. Le formulaire de saisie des communes (frmDataWizard3)

XI-E-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard3
Sélectionnez
Option Compare Database
Option Explicit

Private Const PREVIOUS_FORM_ID                         As Integer = 2
Private Const NEXT_FORM_ID                             As Integer = 4
Private Const TARGET_TABLE                             As String = "tblCommunesSite"
Private Const FIELD_COLLECTION                         As String = "IDCommune;NoSite"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"
Private Const DELETE_RECORD_SQL                        As String = "DELETE * FROM " & TARGET_TABLE & " "
Private Const WHERE_CONDITION                          As String = "WHERE IDCommune = #1 AND NoSite = #2"
Private Const FORM_CAPTION                             As String = "Saisie des données : étape #1/" & MAX_STEP_WIZ

Private Const FORM_SUBJECT                             As String = "des communes du site"
Private Const FORM_TITLE                               As String = " Assistant de saisie des données " & FORM_SUBJECT

XI-E-2. Les événements de formulaire

La procédure Sur_Activation()

Elle est invoquée et se traduit par le code suivant :

Form_Current
Sélectionnez
Private Sub Form_Current()
'On appelle la procédure événementielle générique du même nom
    Call FormCurrent(Me, "IDDepartement")
End Sub

Quelques explications…

On appelle la procédure générique FormCurrent avec le paramètre Me pour identifier le formulaire en cours et le nom du contrôle devant être sollicité dans celle-ci.

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim strErrMessage                                      As String
    'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    If FormDataErrors(DataErr, strErrMessage, Response, Me) Then
        MsgBox strErrMessage, vbExclamation, "Erreur de saisie"
        Me.ActiveControl.Undo
    End If
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On désactive les alertes
    DoCmd.SetWarnings False
    'On masque ou affiche les contrôles selon le mode (Prod ou Test)
    Me.txtTotalRows.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtIDCommune.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtCommune.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtRowCompleted.Visible = SHOW_HIDDEN_CONTROLS

    'Le formulaire est raccordé à la source de données de la constante
    Me.RecordSource = RECORD_SOURCE
    'On applique le titre de la barre de titre dynamiquement
    Me.Caption = Replace(FORM_CAPTION, "#1", NEXT_FORM_ID - 1)
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
End Sub

Quelques explications…

On affecte la visibilité des contrôles de sous-totaux via la constante SHOW_HIDDEN_CONTROLS.

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

La procédure Sur_Minuterie()

Form_Timer
Sélectionnez
Private Sub Form_Timer()
Dim lngCount                                           As Long
    'On provoque un clignotement du texte de complétion des données
    Me.lblRecCompleted.Visible = Not lblRecCompleted.Visible
End Sub

Quelques explications…

Cet événement provoque ici le clignotement du texte de complétion des données selon la valeur du champ situé dans le sous-formulaire.

Il n'y a pas d'autre événement pour ce formulaire.

XI-F. Le sous-formulaire de saisie des communes (frmDataWizard3_sub)

XI-F-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard3_sub
Sélectionnez
Option Compare Database
Option Explicit

Private Const TARGET_TABLE                             As String = "tblCommunesSite"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"

XI-F-2. Les événements

La procédure Sur_Activation()

Elle est invoquée et se traduit par le code suivant :

Form_Current
Sélectionnez
Private Sub Form_Current()
'On appelle la procédure événementielle générique du même nom
    Call SubFormCurrent(Me, "cboDepartement", False)
End Sub

Quelques explications…

On appelle la procédure générique FormCurrent avec le paramètre Me pour identifier le formulaire en cours et le nom du contrôle devant être sollicité dans celle-ci.

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    SubFormDataError Me, DataErr, Response, "même département / même commune / même  de site"
End Sub

Quelques explications…

Cet événement appelle SubFormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On affecte la source de données
    Me.RecordSource = RECORD_SOURCE
End Sub

Quelques explications…

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

Il n'y a pas d'autre événement pour ce formulaire.

XI-G. Le formulaire de saisie des contrats (frmDataWizard4)

XI-G-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard4
Sélectionnez
Option Compare Database
Option Explicit

Private Const PREVIOUS_FORM_ID                         As Integer = 3
Private Const NEXT_FORM_ID                             As Integer = 5
Private Const TARGET_TABLE                             As String = "tblContrats"
Private Const FIELD_COLLECTION                         As String = "IDContrat;LibelleContrat;IDTypeContrat;DateDebut;DateFin"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE
Private Const FORM_CAPTION                             As String = "Saisie des données : étape #1/" & MAX_STEP_WIZ

Private Const FORM_SUBJECT                             As String = "des Contrats"
Private Const FORM_TITLE As String = " Assistant de saisie des données " & FORM_SUBJECT

XI-G-2. Les événements de formulaire

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim strErrMessage                                      As String
    'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    If FormDataErrors(DataErr, strErrMessage, Response, Me) Then
        MsgBox strErrMessage, vbExclamation, "Erreur de saisie"
        Me.ActiveControl.Undo
    End If
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
Dim oRS                                                As DAO.Recordset
Dim lngRecordCount                                     As Long

'Le formulaire est raccordé à la source de données de la constante
    Me.RecordSource = RECORD_SOURCE
    'On applique le titre de la barre de titre dynamiquement
    Me.Caption = Replace(FORM_CAPTION, "#1", NEXT_FORM_ID - 1)
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
    
    'On compte le nombre de contrats afin de libérer le bouton donnant accès à la liste
    Set oRS = Me.RecordsetClone
    With oRS
        If Not .EOF Then
            .MoveLast
            lngRecordCount = .RecordCount
            .MoveFirst
        End If
        .Close
    End With
    Me.cmdShowExistingContracts.Enabled = (lngRecordCount > 1)
    Set oRS = Nothing
End Sub

Quelques explications…

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

On compte alors le nombre d'enregistrements que l'on affecte à la variable lngRecordCount dont la valeur détermine l'activation du bouton cmdShowExistingContracts permettant d'afficher un popup si elle supérieure à 1.

Il n'y a pas d'autre événement pour ce formulaire.

XI-H. Le formulaire listant les contrats (frmDataWizard4_Popup)

XI-H-1. L'en-tête de la feuille

L'en-tête ici ne comporte aucune déclaration.

frmDataWizard4_Popup
Sélectionnez
Option Compare Database
Option Explicit

XI-H-2. Les événements

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
    Me.txtFocus.SetFocus
End Sub

Quelques explications…

On donne le focus au contrôle txtFocus dissimulé, mais pas invisible (enfin, il faut de bons yeux tout de même).

Il est situé en haut et à gauche avec des dimensions et positions, toutes définies à zéro.

Il n'y a pas d'autre événement pour ce formulaire.

XI-I. Le formulaire de saisie de l'association Contrat-Communes (frmDataWizard5)

XI-I-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard5
Sélectionnez
Option Compare Database
Option Explicit

Private Const PREVIOUS_FORM_ID                         As Integer = 4
Private Const NEXT_FORM_ID                             As Integer = MAX_STEP_WIZ + 1
Private Const FINAL_FORM_ID                            As Integer = LAST_FORM_INDEX
Private Const TARGET_TABLE                             As String = "tblContratCommunes"
Private Const FIELD_COLLECTION                         As String = "IDCommune;IDContrat"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"
Private Const DELETE_RECORD_SQL                        As String = "DELETE * FROM " & TARGET_TABLE & " "
Private Const WHERE_CONDITION                          As String = "WHERE IDCommune = #1 AND IDContrat = '#2'"
Private Const FORM_CAPTION                             As String = "Saisie des données : étape #1/" & MAX_STEP_WIZ

Private Const FORM_SUBJECT                             As String = "des contrats des communes du site"
Private Const FORM_TITLE As String = " Assistant de saisie des données " & FORM_SUBJECT

XI-I-2. Les événements de formulaire

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim strErrMessage                                      As String
    'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    If FormDataErrors(DataErr, strErrMessage, Response, Me) Then
        MsgBox strErrMessage, vbExclamation, "Erreur de saisie"
        Me.ActiveControl.Undo
    End If
End Sub

Quelques explications…

Cet événement appelle FormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On désactive les alertes
    DoCmd.SetWarnings False
    'On masque ou affiche les contrôles selon le mode (Prod ou Test)
    Me.txtTotalRows.Visible = SHOW_HIDDEN_CONTROLS
    'Me.txtIDCommune.Visible = SHOW_HIDDEN_CONTROLS
    'Me.txtCommune.Visible = SHOW_HIDDEN_CONTROLS
    Me.txtRowCompleted.Visible = SHOW_HIDDEN_CONTROLS

    'Le formulaire est raccordé à la source de données de la constante
    Me.RecordSource = RECORD_SOURCE
    'On applique le titre de la barre de titre dynamiquement
    Me.Caption = Replace(FORM_CAPTION, "#1", NEXT_FORM_ID - 1)
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
End Sub

Quelques explications…

On affecte la visibilité des contrôles de sous-totaux via la constante SHOW_HIDDEN_CONTROLS.

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

La procédure Sur_Minuterie()

Elle est invoquée et se traduit par le code suivant :

Form_Timer
Sélectionnez
Private Sub Form_Timer()
    'On provoque un clignotement du texte de complétion des données
    Me.lblRecCompleted.Visible = Not lblRecCompleted.Visible
End Sub

Quelques explications…

Cet événement provoque ici le clignotement du texte de complétion des données selon la valeur du champ situé dans le sous-formulaire.

XI-J. Le sous-formulaire de saisie des communes (frmDataWizard5_sub)

XI-J-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmDataWizard5_sub
Sélectionnez
Private Const TARGET_TABLE                             As String = "tblContratCommunes"
Private Const RECORD_SOURCE                            As String = "SELECT * FROM " & TARGET_TABLE & ";"

XI-J-2. Les événements

La procédure Sur_Activation()

Elle est invoquée et se traduit par le code suivant :

Form_Current
Sélectionnez
Private Sub Form_Current()
'On appelle la procédure événementielle générique du même nom
    Call SubFormCurrent(Me, "Contrat / Communes", False)
End Sub

Quelques explications…

On appelle la procédure générique FormCurrent avec le paramètre Me pour identifier le formulaire en cours et le nom du contrôle devant être sollicité dans celle-ci.

La procédure Sur_Erreur ()

Elle est invoquée et se traduit par le code suivant :

Form_Error
Sélectionnez
Private Sub Form_Error(DataErr As Integer, Response As Integer)
'On appelle FormDataErrors() selon DataErr si une erreur est rencontrée 
    SubFormDataError Me, DataErr, Response, "même département / même commune / même  de site"
End Sub

Quelques explications…

Cet événement appelle SubFormDataErrors() avec le paramètre DataErr si une erreur est rencontrée et affiche un message circonstanciel.

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
    'On affecte la source de données
    Me.RecordSource = RECORD_SOURCE
End Sub

Quelques explications…

Le formulaire voit sa propriété RecordSource affectée à la constante du même nom RECORD_SOURCE.

Il n'y a pas d'autre événement pour ce formulaire.

XI-K. Le formulaire de fin (frmEnd)

XI-K-1. L'en-tête de la feuille

L'en-tête ici comporte le lot de constantes définies dans ce tableau.

frmEnd
Sélectionnez
Private Const PREVIOUS_FORM_ID                         As Integer = 5
Private Const NEXT_FORM_ID                             As Integer = 0
Private Const FORM_CAPTION                             As String = "Saisie des données  : dernière étape"
Private Const FORM_SUBJECT                             As String = "pour l'ensemble des modules"
Private Const FORM_TITLE                               As String = "Fin de l'Assistant de saisie des données " & FORM_SUBJECT

Private m_blnCompleted                                 As Boolean
Private m_intCount                                     As Integer

Quelques explications…

La variable m_blnCompleted est utilisée pour déterminer la complétude des étapes et la variable m_intCount pour les compter.

XI-K-2. Les événements

La procédure Sur_Chargement()

Elle est invoquée et se traduit par le code suivant :

Form_Load
Sélectionnez
Private Sub Form_Load()
'On applique le titre de la barre de titre dynamiquement
    Me.Caption = FORM_CAPTION
    'On applique le titre de la fenêtre dynamiquement
    Me.lblTitle.Caption = FORM_TITLE
    'On initialise la variable selon la résultante de la fonction
    m_blnCompleted = AllStepsAreCompleted(m_intCount)
    'On affiche la liste des étapes manquantes s'il y en a
    If Not m_blnCompleted Then
        Me.lblStepList.Caption = g_strDataPage
        lblStepStatus.Visible = True
    Else
        Me.lblStepList.Caption = "Aucune étape n'a été omise."
        lblStepStatus.Visible = False
    End If
End Sub

Quelques explications…

La propriété Légende (titre de la fenêtre) voit sa valeur modifiée via la constante FORM_CAPTION.

Le titre (lblTitle) dessiné dans le formulaire est lui aussi alimenté dynamiquement via la constante FORM_TITLE.

On affecte à la variable m_blnCompleted le fait que toutes les étapes sont complétées et on change l'intitulé des étiquettes en conséquence.

XII. Les procédures événementielles des contrôles

XII-A. Le formulaire d'accueil (frmStart)

XII-A-1. L'en-tête de la feuille

L'en-tête ici comporte l'index du formulaire suivant et le titre de la fenêtre dans deux constantes dûment nommées.

frmStart
Sélectionnez
Option Compare Database
Option Explicit

Private Const NEXT_FORM_ID                             As Integer = 1
Private Const FORM_CAPTION                             As String = "Écran d'accueil"

XII-A-2. Les événements

La procédure événementielle des boutons est systématiquement appelée via l'événement SurClic() ; ces procédures se traduisent par les blocs de code suivants :

Le bouton Quitter

cmdQuit_Click
Sélectionnez
Private Sub cmdQuit_Click()
Dim strMsgData                                         As String
Dim intCount                                           As Integer
    'Appelle la fonction pour déterminer si toutes les étapes sont complétées
    If AllStepsAreCompleted(intCount) Then
        'La chaîne prend alors le message correspondant (ici positif)
        strMsgData = vbCrLf & vbCrLf & "Tous les modules sont complétés"
    Else
        'La chaîne prend alors le message correspondant (ici négatif)
        strMsgData = vbCrLf & vbCrLf & "Toutes les données n'ont pas été saisies." & vbCrLf & vbCrLf & IIf(intCount = 0, "Aucune étape n'a été correctement remplie !", Trim(str(intCount)) & " étape(s) a été correctement remplie(s) sur un total de " & MAX_STEP_WIZ & ".")
    End If
    'On demande confirmation avant de quitter l'application
    If MsgBox("Êtes-vous certain de vouloir quitter ?" & strMsgData, vbQuestion + vbYesNo + vbDefaultButton2, "Quitter") = vbYes Then
        Application.Quit acQuitSaveAll
    End If
End Sub

Quelques explications…

On appelle la fonction AllStepsAreCompleted() pour prévenir (toujours de façon optimiste) la complétude des données, avant de quitter l'application, par un message à l'utilisateur.

Le bouton Démarrer

cmdStart_Click
Sélectionnez
Private Sub cmdStart_Click()
Dim strErrDescription                                  As String

    On Error GoTo L_ErrcmdStart_Click
    'Si la remise à blanc des données est demandée...
    If Nz(Me.chkRAZData, False) Then
        'On confirme par un message la remise à blanc avant de passer la première étape
        Select Case MsgBox("Êtes-vous certain de vouloir supprimer toutes les données déjà inscrites avant de continuer ?" & vbCrLf & _
                           "Cette action est irréversible...", vbExclamation + vbYesNoCancel + vbDefaultButton2, "Effacer toutes les données")
            Case vbYes
                'On appelle la fonction qui retourne un succès ou un échec
                If EraseAllDataBefore(strErrDescription) = False Then
                    'On génère l'erreur d'exécution si la fonction échoue
                    Err.Raise 17, "Remise à blanc", strErrDescription
                End If
            Case vbCancel
                'On ne fait rien
                Exit Sub
            Case Else
        End Select
    End If
    'On ferme cet écran
    DoCmd.Close acForm, Me.Name
    'On ouvre l'écran de l'étape 1
    DoCmd.OpenForm FORM_DATA_WIZ & NEXT_FORM_ID, , , , , acDialog

    On Error GoTo 0
L_ExcmdStart_Click:
    Exit Sub

L_ErrcmdStart_Click:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExcmdStart_Click
End Sub

Quelques explications…

Si la case de remise à blanc est cochée, alors :

  • on confirme par un message cette remise à blanc avant de passer la première étape ;
  • on appelle alors la fonction EraseAllDataBefore qui retourne un succès ou un échec ;
  • on ferme cet écran ;
  • on ouvre l'écran de l'étape 1.

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-B. Le formulaire de saisie du site (frmDataWizard1)

Le contrôle cboDeptRegion

cboDeptRegion_Click
Sélectionnez
Private Sub cboDeptRegion_Click()
Dim SQLListeCommunes                                   As String
Dim strNoDepartement                                   As String

    'On construit la clause SQL selon le département sélectionné
    strNoDepartement = Me.cboDeptRegion
    SQLListeCommunes = "SELECT IDCommune, [NomCommune] & ' (' & [CodePostal] & ')' AS Commune, IDDepartement "
    SQLListeCommunes = SQLListeCommunes & "FROM tbl_ListeDesCommunes WHERE IDDepartement='" & strNoDepartement & "';"
    'On affecte la requête à la liste des communes, filtrée sur le département sélectionné
    With Me.IDCommune
        .RowSource = SQLListeCommunes
        'On active la liste, lui donne le focus et lui force le déroulement
        .Enabled = True
        .SetFocus
        .Dropdown
    End With
End Sub

Quelques explications…

Le contrôle IDCommune se voit affecter la requête source SQL en fonction du numéro du département choisi.

Le bouton Supprimer

cmdDelete_Click
Sélectionnez
Private Sub cmdDelete_Click()
Dim SQLDelete                                          As String
Dim strSubject                                         As String
    'S'il n'y a pas de nullité
    If Not IsNull(Me.NoSite) And Not IsNull(Me.LibelleSite) Then
        strSubject = "le site " & Me.LibelleSite & " (" & Me.NoSite & ") "
        SQLDelete = DELETE_RECORD_SQL & Replace(WHERE_CONDITION, "#1", Me.NoSite)
        'On appelle la méthode DeleteOrSaveRecord avec les paramètres attendus
        DeleteOrSaveRecord Me, 3, Me.NoSite, SQLDelete, strSubject
    End If
End Sub

Quelques explications…

Si l'identifiant cible et le libellé ne sont pas nuls alors :

  • on alimente la chaîne strSubject du contexte qui fera office de confirmation du message de suppression ;
  • on élabore la clause SQL de suppression dans la variable SQLDelete avec les constantes de l'en-tête dûment remplacées par la fonction Replace() ;
  • on appelle la procédure DeleteOrSaveRecord () avec les paramètres attendus.

Le bouton Suivant

cmdNext_Click
Sélectionnez
Private Sub cmdNext_Click()
'On affecte l'identifiant du site à la variable globale (elle est exploitée pour tout l'assistant)
    g_strNoSiteEnCours = Nz(Me.NoSite, "")
    'On suppose (c'est un exemple) que le nom du site est le nom du domaine
    g_strDomaineSiteEnCours = Replace(LCase(Nz(Me.LibelleSite, "")), " ", "") & ".com"
    'On appelle la procédure pour ouvrir le formulaire suivant
    OpenTheForm GotoNext, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, False
End Sub

Quelques explications…

On accède au formulaire suivant à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On appelle la procédure pour ouvrir le formulaire précédent
    OpenTheForm GotoPrevious, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, False
End Sub

Quelques explications…

On accède au formulaire précédent à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le contrôle LibelleSite

LibelleSite_AfterUpdate
Sélectionnez
Private Sub LibelleSite_AfterUpdate()
'On force la casse en majuscules
    Me.LibelleSite = UCase(Me.LibelleSite)
    Me.cmdDelete.Enabled = (Nz(Me.NoSite, 0) And Len(Nz(Me.LibelleSite, "")))
End Sub

Quelques explications…

On force la casse du libellé du site en majuscules et on active le bouton de suppression en fonction de la valeur de deux champs.

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-C. Le formulaire de saisie du personnel (frmDataWizard2)

Le bouton Ajouter

cmdAdd_Click
Sélectionnez
Private Sub cmdAdd_Click()
'On appelle la méthode AddNewOrCancel avec les paramètres attendus
    AddNewOrCancel Me, NEXT_FORM_ID - 1, TARGET_TABLE, FIELD_COLLECTION, "Prenom", True, NEXT_FORM_ID
End Sub

Quelques explications…

On appelle ici la procédure AddNewOrCancel () vue et détaillée ci-après pour ajouter ou annuler un enregistrement.

Le bouton Supprimer

cmdDelete_Click
Sélectionnez
Private Sub cmdDelete_Click()
Dim SQLDelete                                          As String
Dim strSubject                                         As String

    If Not IsNull(Me.txtIDPersonne) Then
        strSubject = "l'employé " & IIf(Len(Nz(Me.txtNomPrenom, "")), " (" & txtNomPrenom & ") ", "non spécifié")
        SQLDelete = DELETE_RECORD_SQL & Replace(Replace(WHERE_CONDITION, "#1", Me.txtIDPersonne), "#2", g_strNoSiteEnCours)
        'On appelle la méthode DeleteOrSaveRecord avec les paramètres attendus
        DeleteOrSaveRecord Me, 3, Me.txtIDPersonne, SQLDelete, strSubject
    End If
End Sub

Quelques explications…

Si l'identifiant cible n'est pas nul alors :

  • on alimente la chaîne strSubject du contexte qui fera office de confirmation du message de suppression ;
  • on élabore la clause SQL de suppression dans la variable SQLDelete avec les constantes de l'en-tête dûment remplacées par la fonction Replace() ;
  • on appelle la procédure DeleteOrSaveRecord () avec les paramètres attendus.

Le bouton Suivant

cmdNext_Click
Sélectionnez
Private Sub cmdNext_Click()
'On appelle la procédure pour ouvrir le formulaire suivant
    OpenTheForm GotoNext, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True
End Sub

Quelques explications…

On accède au formulaire suivant à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On appelle la procédure pour ouvrir le formulaire précédent
    OpenTheForm GotoPrevious, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True
End Sub

Quelques explications…

On accède au formulaire précédent à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

XII-D. Le sous-formulaire de saisie des communes (frmDataWizard2_sub)

Le contrôle chkCompleted

chkCompleted_MouseUp
Sélectionnez
Private Sub chkCompleted_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
'On donne le focus : la case à cocher ne doit pas être cliquée
    Me.Prenom.SetFocus
End Sub

Quelques explications…

Si l'utilisateur tente de cliquer sur la case à cocher, on donne le focus au premier contrôle.

Le contrôle Email

EMAIL_AfterUpdate
Sélectionnez
Private Sub EMAIL_AfterUpdate()
    Me.EMAIL = LCase(Me.EMAIL)
    Me.NoSite = g_strNoSiteEnCours
End Sub

Quelques explications…

On force la casse de l'adresse mail en minuscules et le champ NoSite prend la valeur de la variable globale représentant le numéro du site, initialisée dans le premier formulaire de saisie.

Le contrôle IDPrivileges

IDPrivileges_AfterUpdate
Sélectionnez
Private Sub IDPrivileges_AfterUpdate()
    Me.NoSite = g_strNoSiteEnCours
End Sub

Quelques explications…

Le champ NoSite prend la valeur de la variable globale représentant le numéro du site.

Le contrôle IDPrivileges

IDPrivileges_NotInList
Sélectionnez
Private Sub IDPrivileges_NotInList(NewData As String, Response As Integer)
'On appelle la méthode NotInList pour avertir de la non-correspondance
    Response = NotInList(NewData, "Le droit attribué")
End Sub

Quelques explications…

On appelle ici la fonction NotInList() vue et détaillée ci-après…

Le contrôle Login

Login_AfterUpdate
Sélectionnez
Private Sub Login_AfterUpdate()
    Me.NoSite = g_strNoSiteEnCours
End Sub

Quelques explications…

Le champ NoSite prend la valeur de la variable globale représentant le numéro du site.

Le contrôle NomFamille

NomFamille_AfterUpdate
Sélectionnez
Private Sub NomFamille_AfterUpdate()
'On force en MAJUSCULES
    Me.NomFamille = UCase(Me.NomFamille)
    'On force le champ avec la valeur commune de l'identifiant
    Me.NoSite = g_strNoSiteEnCours
    'On construit l'adresse mail si on peut
    Me.EMAIL = LCase(Nz(Me.Prenom, "???") & "." & Me.NomFamille & "@" & g_strDomaineSiteEnCours)
    'Le login est par défaut le Nom.P
    Me.Login = UCase(Me.NomFamille) & "." & Left(Nz(Me.Prenom, " "), 1)
End Sub

Quelques explications…

On force la casse du nom de famille en majuscules et on finit de composer l'adresse mail fondée sur la règle supposée Email = .

Le contrôle Prenom

Prenom_AfterUpdate
Sélectionnez
Private Sub Prenom_AfterUpdate()
'On force en Nom Propre
    Me.Prenom = StrConv(Me.Prenom, vbProperCase)
    'On force le champ avec la valeur commune de l'identifiant
    Me.NoSite = g_strNoSiteEnCours
    'On construit l'adresse mail si on peut
    Me.EMAIL = LCase(Me.Prenom & "." & Nz(Me.NomFamille, "???") & "@" & g_strDomaineSiteEnCours)
End Sub

Quelques explications…

On force la casse du prénom en nom propre et on finit de composer l'adresse mail fondée sur la règle supposée Email = .

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-E. Le formulaire de saisie des communes (frmDataWizard3)

Le bouton Ajouter

cmdAdd_Click
Sélectionnez
Private Sub cmdAdd_Click()
'On appelle la méthode AddNewOrCancel avec les paramètres attendus
    AddNewOrCancel Me, NEXT_FORM_ID - 1, TARGET_TABLE, FIELD_COLLECTION, "cboDepartement", True, NEXT_FORM_ID
End Sub

Quelques explications…

On appelle ici la procédure AddNewOrCancel () vue et détaillée ci-après pour ajouter ou annuler un enregistrement.

Le bouton Supprimer

cmdDelete_Click
Sélectionnez
Private Sub cmdDelete_Click()
Dim SQLDelete                                          As String
Dim strSubject                                         As String

    If Not IsNull(Me.txtIDCommune) Then
        strSubject = "la commune " & Me.txtCommune
        SQLDelete = DELETE_RECORD_SQL & Replace(Replace(WHERE_CONDITION, "#1", Me.txtIDCommune), "#2", g_strNoSiteEnCours)
        'On appelle la méthode DeleteOrSaveRecord avec les paramètres attendus
        DeleteOrSaveRecord Me, 3, Me.txtIDCommune, SQLDelete, strSubject
    End If
End Sub

Quelques explications…

Si l'identifiant cible n'est pas nul alors :

  • on alimente la chaîne strSubject du contexte qui fera office de confirmation du message de suppression ;
  • on élabore la clause SQL de suppression dans la variable SQLDelete avec les constantes de l'en-tête dûment remplacées par la fonction Replace() ;
  • on appelle la procédure DeleteOrSaveRecord () avec les paramètres attendus.

Le bouton Suivant

cmdNext_Click
Sélectionnez
Private Sub cmdNext_Click()
'On appelle la procédure pour ouvrir le formulaire suivant
    OpenTheForm GotoNext, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True
End Sub

Quelques explications…

On accède au formulaire suivant à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On appelle la procédure pour ouvrir le formulaire précédent
    OpenTheForm GotoPrevious, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True
End Sub

Quelques explications…

On accède au formulaire précédent à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-F. Le sous-formulaire de saisie des communes (frmDataWizard3_sub)

Le contrôle cboDepartement (1)

cboDepartement_AfterUpdate
Sélectionnez
Private Sub cboDepartement_AfterUpdate()
Const TOWN_LIST                                        As String = "SELECT C.IDCommune, [NomCommune] & ' (' & [CodePostal] & ')' AS Commune, C.CodePostal FROM tbl_ListeDepartements AS D INNER JOIN tbl_ListeDesCommunes AS C ON D.IDDepartement = C.IDDepartement WHERE (D.IDDepartement = '#') ORDER BY C.CodePostal;"
Dim strRowSource                                       As String

    'On construit la clause SQL selon le département sélectionné
    strRowSource = Replace(TOWN_LIST, "#", Nz(Me.cboDepartement.Column(0), ""))
    'On affecte la requête à la liste des communes, filtrée sur le département sélectionné
    With Me.IDCommune
        .RowSource = strRowSource
        'On lui donne le focus et lui force le déroulement
        .SetFocus
        .Dropdown
    End With
End Sub

Quelques explications…

Le contrôle IDCommune se voit affecter la requête source SQL en fonction du numéro du département choisi.

Le contrôle cboDepartement (2)

cboDepartement_NotInList
Sélectionnez
Private Sub cboDepartement_NotInList(NewData As String, Response As Integer)
'On appelle la méthode NotInList pour avertir de la non-correspondance
    Response = NotInList(NewData, "Le département")
End Sub

Quelques explications…

On appelle ici la fonction NotInList() vue et détaillée ci-après…

Le contrôle chkCompleted

chkCompleted_MouseUp
Sélectionnez
Private Sub chkCompleted_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
'On donne le focus : la case à cocher ne doit pas être cliquée
    Me.cboDepartement.SetFocus
End Sub

Quelques explications…

Si l'utilisateur tente de cliquer sur la case à cocher, on donne le focus au premier contrôle.

Le contrôle IDCommune (1)

IDCommune_AfterUpdate
Sélectionnez
Private Sub IDCommune_AfterUpdate()
    Me.NoSite = g_strNoSiteEnCours
End Sub

Quelques explications…

Le champ NoSite prend la valeur de la variable globale représentant le numéro du site.

Le contrôle IDCommune (2)

IDCommune_NotInList
Sélectionnez
Private Sub IDCommune_NotInList(NewData As String, Response As Integer)
'On appelle la méthode NotInList pour avertir de la non-correspondance
    Response = NotInList(NewData, "La commune")
End Sub

Quelques explications…

On appelle ici la fonction NotInList() vue et détaillée ci-après…

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-G. Le formulaire de saisie des contrats (frmDataWizard4)

Le contrôle LibelleContrat

LibelleContrat_AfterUpdate
Sélectionnez
Private Sub LibelleContrat_AfterUpdate()
'On force la casse en majuscules
    Me.LibelleContrat = UCase(Me.ibelleContrat)
End Sub

Quelques explications…

La zone de texte se voit attribuer sa valeur en MAJUSCULES.

Le bouton cmdShowExistingContracts

cmdShowExistingContracts_Click
Sélectionnez
Private Sub cmdShowExistingContracts_Click()
    DoCmd.OpenForm Me.Name & "_Popup", , , , , acDialog
End Sub

Quelques explications…

On ouvre le formulaire via l'objet DoCmd et sa méthode OpenForm.

Remarque

Le fait de nommer correctement les objets permet de faciliter la rédaction du code sans galérer à chercher comment tel ou tel objet se nomme et en pouvant en plus, user ici du nom du formulaire parent…

Le bouton Suivant

cmdNext_Click
Sélectionnez
Private Sub cmdNext_Click()
    'On appelle la procédure pour ouvrir le formulaire suivant
    OpenTheForm GotoNext, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, False
End Sub

Quelques explications…

On accède au formulaire suivant à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On appelle la procédure pour ouvrir le formulaire précédent
    OpenTheForm GotoPrevious, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, False
End Sub

Quelques explications…

On accède au formulaire précédent à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

XII-H. Le formulaire listant les contrats (frmDataWizard4_Popup)

Le bouton Fermer

cmdClose_Click
Sélectionnez
Private Sub cmdClose_Click()
    DoCmd.Close acForm, Me.Name
End Sub

Quelques explications…

Ici, on ferme simplement le formulaire.

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-I. Le formulaire de saisie de l'association Contrat-Communes (frmDataWizard5)

Le bouton Ajouter

cmdAdd_Click
Sélectionnez
Private Sub cmdAdd_Click()
'On appelle la méthode AddNewOrCancel avec les paramètres attendus
    AddNewOrCancel Me, NEXT_FORM_ID - 1, TARGET_TABLE, FIELD_COLLECTION, "cboDepartement", True, NEXT_FORM_ID
End Sub

Quelques explications…

On appelle ici la procédure AddNewOrCancel () vue et détaillée ci-après pour ajouter ou annuler un enregistrement.

Le bouton Supprimer

cmdDelete_Click
Sélectionnez
Private Sub cmdDelete_Click()
Dim SQLDelete                                          As String
Dim strSubject                                         As String

    If Not IsNull(Me.txtIDCommune) Then
        strSubject = "Le contrat " & Me.txtIDContrat & "de la commune " & Me.txtNomCommune
        SQLDelete = DELETE_RECORD_SQL & Replace(Replace(WHERE_CONDITION, "#1", Me.txtIDCommune), "#2", Me.txtIDContrat)
        'On appelle la méthode DeleteOrSaveRecord avec les paramètres attendus
        DeleteOrSaveRecord Me, 3, Me.txtIDCommune, SQLDelete, strSubject
    End If
End Sub

Quelques explications…

Si l'identifiant cible n'est pas nul alors :

  • on alimente la chaîne strSubject du contexte qui fera office de confirmation du message de suppression ;
  • on élabore la clause SQL de suppression dans la variable SQLDelete avec les constantes de l'en-tête dûment remplacées par la fonction Replace() ;
  • on appelle la procédure DeleteOrSaveRecord () avec les paramètres attendus.

Le bouton Suivant

cmdNext_Click
Sélectionnez
Private Sub cmdNext_Click()
'On appelle la procédure pour ouvrir le formulaire suivant
    OpenTheForm GotoNext, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True, False
End Sub

Quelques explications…

On accède au formulaire suivant à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On appelle la procédure pour ouvrir le formulaire précédent
    OpenTheForm GotoPrevious, Me, NEXT_FORM_ID, PREVIOUS_FORM_ID, TARGET_TABLE, FIELD_COLLECTION, True, False
End Sub

Quelques explications…

On accède au formulaire précédent à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-J. Le sous-formulaire de saisie des communes (frmDataWizard5_sub)

Le contrôle chkCompleted

chkCompleted_MouseUp
Sélectionnez
Private Sub chkCompleted_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Me.IDContrat.SetFocus
End Sub

Quelques explications…

Si l'utilisateur tente de cliquer sur la case à cocher, on donne le focus au premier contrôle.

Le contrôle IDCommune

IDCommune_NotInList
Sélectionnez
Private Sub IDCommune_NotInList(NewData As String, Response As Integer)
'On appelle la méthode NotInList pour avertir de la non-correspondance
    Response = NotInList(NewData, "La commune")
End Sub

Quelques explications…

On appelle ici la fonction NotInList() vue et détaillée ci-après…

Le contrôle IDContrat

IDContrat_NotInList
Sélectionnez
Private Sub IDContrat_NotInList(NewData As String, Response As Integer)
'On appelle la méthode NotInList pour avertir de la non-correspondance
    Response = NotInList(NewData, "Le contrat")
End Sub

Quelques explications…

On appelle ici la fonction NotInList() vue et détaillée ci-après…

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XII-K. Le formulaire de fin (frmEnd)

Le bouton Début

cmdFirst_Click
Sélectionnez
Private Sub cmdFirst_Click()
'On accède au premier écran
    DoCmd.Close acForm, Me.Name
    DoCmd.OpenForm FORM_START, , , , , acDialog
End Sub

Quelques explications…

Très simplement, ici on ferme ce formulaire et on ouvre le premier, c'est-à-dire celui de l'accueil défini par la constante FORM_START.

Le bouton Précédent

cmdPrevious_Click
Sélectionnez
Private Sub cmdPrevious_Click()
'On bascule sur l'écran précédent selon l'index
    If PREVIOUS_FORM_ID Then
        DoCmd.Close acForm, Me.Name
        DoCmd.OpenForm FORM_DATA_WIZ & PREVIOUS_FORM_ID, , , , , acDialog
    Else
        DoCmd.Close acForm, Me.Name
        DoCmd.OpenForm FORM_START, , , , , acDialog
    End If
End Sub

Quelques explications…

S'il existe une constante PREVIOUS_FORM_ID avec une valeur différente de zéro alors on considère qu'il y a un formulaire à afficher autre que celui de l'accueil ; sinon, on accède à ce dernier.

On accède alors au formulaire ciblé à l'aide de la fonction OpenTheForm () vue et détaillée ci-après…

Le bouton Terminer

cmdEnd_Click
Sélectionnez
Private Sub cmdEnd_Click()
Dim strMsgData                                         As String

    'On vérifie que tout est rempli par étape :
    'On initialise le message en conséquence
    If m_blnCompleted Then
        strMsgData = "Tous les modules sont complétés" & vbCrLf & vbCrLf & "Voulez-vous quitter l'application ?"
    Else
        strMsgData = "Toutes les données n'ont pas été saisies." & vbCrLf & vbCrLf & IIf(m_intCount = 0, "Aucune étape n'a été correctement remplie !", Trim(str(m_intCount)) & " étape(s) a été correctement remplie(s) sur un total de " & MAX_STEP_WIZ & ".") & vbCrLf & vbCrLf & "Êtes-vous certain de vouloir quitter ?"
    End If
    'On affiche alors le message
    If MsgBox(strMsgData, 292, "Quitter") = 6 Then
        'On quitte
        Application.Quit acQuitSaveAll
    End If
End Sub

Quelques explications…

Selon l'état de m_blnCompleted, le message de la variable strMsgData change.

Un MsgBox est affiché pour confirmer la fermeture de l'application.

Il n'y a pas d'autre événement sur les contrôles pour ce formulaire.

XIII. Les fonctions et procédures locales au sein des différents formulaires

Seul le formulaire de saisie du Site possède une procédure privée locale.

XIII-A. Le formulaire de saisie du site (frmDataWizard1)

SIGNET InitIDCommune

XIII-A-1. La procédure locale InitIDCommune

InitIDCommune
Sélectionnez
Private Sub InitIDCommune()
Dim strIDDepartement                                   As String
    'Vérifie si une commune a déjà été spécifiée et agit en conséquence sur
    'la zone modifiable (qui est indépendante) qui contient les départements 
    With Me.IDCommune
        If Nz(.Value, 0) Then
            strIDDepartement = .Column(2)
            .Enabled = True
        End If
    End With
    Me.cboDeptRegion.Value = strIDDepartement
End Sub

Quelques explications…

Cette procédure vérifie s'il y a une commune définie pour le site et le cas échéant, affecte la valeur du département et de la région en conséquence dans la zone de liste cboDeptRegion, du fait que celle-ci est indépendante et donc attachée à aucune source de contrôle.

Il est à noter que lors de la première utilisation, chacune des zones reste vide.

XIV. Les modules externes

Dans ce projet se trouvent deux modules :

  • un dédié aux fonctions diverses ;
  • l'autre dédié à encapsuler l'ensemble des fonctions et procédures liées directement aux formulaires.

XIV-A. VII-A. Le Module basFormsFunctions

XIV-A-1. L'en-tête de la feuille

L'en-tête ici ne comporte aucune constante ou variable.

basFormsFunctions
Sélectionnez
'**********************************************************************
' Module            : basFormsFunctions
' Type              : Module
' DateTime          : 30/07/2015
' Author            : Argyronet
' Review            : 23/09/2015 - Jean-Philippe AMBROSINO
' Review date       : 27/11/2015
' Purpose           : Module contenant les procédures et fonctions génériques [événementielles] des formulaires
'**********************************************************************
Option Compare Database
Option Explicit

XIV-A-2. La procédure événementielle FormCurrent

Cette procédure se substitue à la procédure événementielle Form_Current qui se traduit dans les propriétés événementielles par Sur activation.

Dans ce projet, cette procédure est uniquement utilisée au sein des formulaires.

On affecte à l'objet oSubForm le formulaire basé sur la constante SUBFORM_DATA_WIZ puisque tous les sous-formulaires portent le même nom, quel que soit le formulaire parent.

Ceci est plus commode pour la souplesse du code et de l'usage d'appels génériques d'objets comme les formulaires.

L'objet DoCmd exploite la méthode GoToRecord avec le paramètre acLast afin de positionner le sous-formulaire sur le dernier enregistrement.

Il donne alors le focus au contrôle FirstControl passé en paramètre.

FormCurrent
Sélectionnez
Public Sub FormCurrent(ByRef TargetForm As Form, ByVal FirstControl As String)
'---------------------------------------------------------------------------
' Sub       : FormCurrent
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Procédure générique de substitution de l'événement Current d'un formulaire simple
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 FirstControl : Nom du 1er contrôle

' Return Codes  :  None
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim oSubForm                                           As SubForm
Dim oControl                                           As Control

    With TargetForm
        'On définit le sous-formulaire
        Set oSubForm = .Form(SUBFORM_DATA_WIZ)
        'On donne le focus au sous-formulaire
        With oSubForm
            .SetFocus
            On Error Resume Next
            'On pointe le dernier enregistrement s'il en existe (erreur optimiste)
            DoCmd.GoToRecord , , acLast
            'On définit le contrôle auquel donner le focus
            Set oControl = .Controls(FirstControl)
            oControl.SetFocus
        End With
    End With
    'Libération des objets
    Set oControl = Nothing
    Set oSubForm = Nothing
End Sub

XIV-A-3. La procédure événementielle SubFormCurrent

Cette procédure se substitue elle aussi à la procédure événementielle Form_Current qui se traduit dans les propriétés événementielles par Sur activation.

Dans ce projet, cette procédure est uniquement utilisée au sein des sous-formulaires.

Comme il est fort probable qu'une erreur soit levée par le fait que le formulaire réagit en fonction des données, on pose une instruction d'erreur optimiste.

En effet, les fonctions de domaines sont susceptibles de renvoyer les erreurs à partir du moment où les champs ne permettent pas d'établir le calcul attendu.

On emploie alors l'instruction On Error Resume Next parce qu'ici, elle est maîtrisée.

Rappel
Vous ne devez employer cette instruction que si, et seulement si, vous en maîtrisez l'exécution.

Cet événement est produit lorsque le focus passe à un enregistrement donné pour en faire l'enregistrement activé, ou lorsque le formulaire est actualisé ou qu'il fait l'objet d'une nouvelle requête.

  • La variable lngCount récupère la valeur du champ txtCountRows et la variable blnCompleted récupère la valeur du champ txtCompleted, tous deux situés dans le formulaire parent.
  • Par prudence, et surtout pour les débutants, j'ai greffé l'interception de l'erreur 2147352567 afin de prévenir le développeur qu'il a oublié un truc quelque part…
    Dans l'absolu, cette erreur ne devrait jamais être levée.
  • Si lngCount retourne une valeur, alors le contrôle étiquette lblCountRows reçoit une chaîne récapitulant le nombre d'enregistrements en genre et en nombre et sa couleur passe du vert au rouge selon que la ligne est complétée ou non.
SubFormCurrent
Sélectionnez
Public Sub SubFormCurrent(ByRef TargetSubForm As Form, ByVal ItemData As String, ByVal IsFemale As Boolean)
'---------------------------------------------------------------------------
' Sub       : SubFormCurrent
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Procédure générique de substitution de l'événement Current d'un formulaire simple
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 ItemData : Sujet du contenu
'                 IsFemale : True pour changer le genre des textes

' Return Codes  :  None
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim lngCount                                           As Long
Dim blnCompleted                                       As Boolean

    'On pose une gestion d'erreurs optimiste contrôlée
    On Error Resume Next
    'On compte le nombre d'enregistrements
    lngCount = Nz(TargetSubForm.txtCountRows, 0)
    'On vérifie si la ligne en cours est complète
    blnCompleted = Nz(TargetSubForm.txtCompleted, False)
    'Il est probable qu'une erreur définie par l'application ou par l'objet soit levée :
    'on la gère de façon optimiste et n'en tient pas compte
    If Err = -2147352567 Then
        MsgBox "Attention..." & vbCrLf & "Vérifier que la source de contrôle du sous-formulaire '" & TargetSubForm.Name & "' est correcte !", vbExclamation, "Message développeur"
        Err.Clear
    End If
    'S'il existe des enregistrements
    If lngCount Then
        With TargetSubForm.Parent
            'On adapte l'intulé  du label avec le nombre
            .lblCountRows.Caption = lngCount & ItemData & "(s) enregistré" & IIf(IsFemale, "e", "") & "(s)"
            With .lblRecCompleted
                'On adapte l'intitulé du label avec le texte et la couleur correspondante
                .Caption = "Ligne " & IIf(blnCompleted, "complète", "incomplète")
                .ForeColor = IIf(blnCompleted, CLR_GREENCOLOR, CLR_REDCOLOR)
            End With
            .cmdDelete.Caption = CMD_DELETE
        End With
    End If
End Sub

XIV-A-4. La procédure OpenTheForm

Cette procédure publique permet d'ouvrir un formulaire de façon personnalisée.

Elle est utilisée ici dans la navigation depuis les boutons intitulés Précédent et Suivant avec par le paramètre GotoDirection.

Avant de fermer le formulaire en cours et ouvrir l'autre, selon que l'on ait affaire à un formulaire simple ou un sous-formulaire, les fonctions DataFormCompleted() ou DataSubFormCompleted() sont appelées afin d'avertir, par l'intermédiaire d'un MsgBox(),que les données ne sont pas complétées comme il se doit.

Du fait que ce message permette d'ignorer, l'utilisateur peut faire abstraction de cette alerte et continuer pour passer effectivement au formulaire qu'il a choisi, en cliquant respectivement sur le bouton approprié dans le sens de la navigation souhaitée, selon la valeur du paramètre GotoDirection.

Le formulaire courant est alors fermé :

  • un examen du paramètre IsLastStep est effectué afin de vérifier si l'on est à la dernière étape ou non, auquel cas on ouvre le formulaire de fin frmEnd ;
  • sinon, on ouvre le formulaire basé sur l'index.
OpenTheForm
Sélectionnez
Public Sub OpenTheForm(ByVal GotoDirection As ge_FormDirection, ByRef TargetForm As Form, ByVal NextFormID As Long, ByVal PreviousFormID As Long, ByVal TableSource As String, ByVal FieldCollection As String, ByVal FormWithSubForm As Boolean, Optional ByVal IsLastStep As Boolean = False)
'---------------------------------------------------------------------------
' Sub       : OpenTheForm
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Procédure générique de substitution de la méthode OpenForm
'...........................................................................
' Parameters    : GotoDirection : Sens (précédent/suivant)
'                 TargetForm : Formulaire cible
'                 NextFormID : ID du formulaire suivant
'                 PreviousFormID : ID du formulaire précédent
'                 TableSource : Table ou requête source
'                 FieldCollection : Noms des champs utilisés
'                 FormWithSubForm = True s'il y a un sous-formulaire

' Return Codes  :  None
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim SQLSelect                                          As String
Dim intFormIndex                                       As Integer

    On Error GoTo L_ErrOpenTheForm

    'On établit la clause Select fondée sur la table source
    SQLSelect = "SELECT * FROM " & TableSource
    'Si l'on vérifie le sous-formulaire...
    If FormWithSubForm Then
        'Si tous les champs du sous-formulaire ne sont pas renseignés...
        If DataSubFormCompleted(TableSource, FieldCollection, False, NextFormID, IsLastStep) = vbNo Then Exit Sub
    Else
        'Si tous les champs du formulaire ne sont pas renseignés...
        If DataFormCompleted(TargetForm, False, FieldCollection, NextFormID, IsLastStep) = vbNo Then Exit Sub
    End If
    'Selon le sens de navigation, on définit la variable de l'index du formulaire cible
    If GotoDirection = GotoNext Then
        intFormIndex = NextFormID
    Else
        intFormIndex = PreviousFormID
    End If
    'On ferme ce formulaire
    DoCmd.Close acForm, TargetForm.Name

    'Si l'on arrive à la dernière étape...
    If IsLastStep Then
        'Dernier identifiant : on ouvre le formulaire de fin
        DoCmd.OpenForm FORM_END, , , , , acDialog
    Else
        'On ouvre le suivant ou précédent
        If intFormIndex Then
            DoCmd.OpenForm FORM_DATA_WIZ & intFormIndex, , , , , acDialog
        Else
            DoCmd.OpenForm FORM_START, , , , , acDialog
        End If
    End If

    On Error GoTo 0
L_ExOpenTheForm:
    Exit Sub

L_ErrOpenTheForm:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExOpenTheForm
End Sub

XIV-A-5. La fonction événementielle FormDataErrors()

Cette procédure générique se substitue de façon enrichie à l'événement Form_Error().

Du fait de la différence possible entre formulaire simple et sous-formulaire, cette fonction est indépendante.

On utilise ici un Select Case où l'on passe des conditions émises par des constantes dont les valeurs sont expressément les valeurs typiquement rencontrées dans cet événement.

Je suis bien conscient qu'il peut y en avoir d'autres, d'où le Case Else (à ne jamais oublier dans un tel bloc), mais pour ce projet tel que développé, cela n'entrait pas en considération.

La fonction retourne la valeur conséquente à la valeur logique de (Response = acDataErrContinue).

FormDataError
Sélectionnez
Public Function FormDataErrors(ByVal DataErr As Integer, ByRef Message As String, ByRef Response As Integer, Optional ByRef TargetForm As Form) As Boolean
'---------------------------------------------------------------------------
' Function      : FormDataErrors
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Procédure générique de substitution de l'événement Form_Error
'...........................................................................
' Parameters    : DataErr : Erreur de données
'                 Message : Message de retour
'                 Response : Flag de comportement de la réponse suite à l'erreur
'                 TargetForm : Formulaire cible

' Return Codes  : Boolean = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Const RESET_VBA_CODE                                   As Integer = 29013
Const INDEX_DOUBLONS                                   As Integer = 3022
Const INPUT_MASK_VIOLATION                             As Integer = 2279
Const NOT_IN_LIST                                      As Integer = 2237
Const INVALID_INPUTMASK                                As Integer = 2113

    Message = vbNullString

    Select Case DataErr
            'Non-respect du masque de saisie
        Case INPUT_MASK_VIOLATION
            Response = acDataErrContinue
            Message = "Attention !" & vbCrLf & "Le masque de saisie du champ doit être respecté."
            'Élément absent de la liste
        Case NOT_IN_LIST
            Response = acDataErrContinue
            Message = "Attention !" & vbCrLf & "Veuillez choisir un élément dans la liste..."
            'Doublon ou tentative d'action de réinitialisation du code en cours d'exécution
        Case INDEX_DOUBLONS, RESET_VBA_CODE
            Response = acDataErrDisplay
            Message = "Attention !" & vbCrLf & "Vous avez déjà saisi ou bien il existe un enregistrement identique dans la base !"
            'Mauvais usage du masque de saisie
        Case INVALID_INPUTMASK
            Response = acDataErrContinue
            Message = "Attention !" & vbCrLf & "La valeur est non valide pour ce champ..." & vbCrLf & vbCrLf & "Vous avez peut-être entré du texte dans un champ numérique ou un nombre supérieur à ce que permet la taille du champ ou encore une date incohérente."
            'Toute autre cause...
        Case Else
            Response = acDataErrContinue
            Message = "Attention !" & vbCrLf & "La valeur que vous avez tapée est incorrecte..."
            If Not TargetForm Is Nothing Then
                With TargetForm.ActiveControl
                    On Error Resume Next
                    'On annule le contenu du champ s'il est accessible
                    .Undo
                    Err.Clear
                    .SetFocus
                End With
            End If
    End Select
    'Valeur de retour
    FormDataErrors = (Response = acDataErrContinue)
End Function

XIV-A-6. La procédure événementielle SubFormDataError

Cette procédure générique se substitue de façon enrichie à l'événement Form_Error() et est écrite dans le même esprit que la fonction ci-avant FormDataErrors() à ceci près qu'elle est exploitée uniquement par les sous-formulaires.

SubFormDataError
Sélectionnez
Public Sub SubFormDataError(TargetForm As Form, DataErr As Integer, Response As Integer, ByVal TargetDataName As String)
'---------------------------------------------------------------------------
' Function      : SubFormDataError
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Procédure générique de substitution de l'événement Form_Error pour les sous-formulaires
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 DataErr : Erreur de données
'                 Response : Flag de comportement de la réponse suite à l'erreur
'                 TargetDataName : Sujet de la donnée concernée
'
' Return Codes  : Boolean = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Const INVALID_INPUTMASK                                As Integer = 2113
Const INDEX_DOUBLONS                                   As Integer = 3022
Const VALUE_IS_REQUIRED                                As Integer = 3314

    Select Case DataErr
        Case INVALID_INPUTMASK
            'Ne devrait jamais arriver sauf si vous avez inversé la colonne liée
            MsgBox "Attention : " & vbCrLf & "Valeur non valide pour ce champ.Vous avez peut-être entré du texte dans un champ numérique ou un nombre supérieur à ce que permet sa taille.", vbExclamation, "Valeur incompatible"
        Case VALUE_IS_REQUIRED
            'Donnée requise
            If MsgBox("Vous devez d'abord inscrire une valeur dans chaque champ." & vbCrLf & vbCrLf & "Voulez-vous annuler cet enregistrement ?", 52, "Ligne incomplète") = 6 Then
                On Error Resume Next
                DoCmd.RunCommand acCmdUndo
                On Error GoTo 0
                With TargetForm.Parent
                    .cmdAdd.Caption = CMD_ADD
                    .cmdDelete.Caption = CMD_DELETE
                End With
            End If
        Case INDEX_DOUBLONS
            'Risque de doublons dans champs index
            MsgBox "Attention : " & vbCrLf & "Vous avez créé un doublon :" & vbCrLf & "(" & TargetDataName & ")." & vbCrLf & "Veuillez modifier votre saisie ou bien annuler la ligne (Echap).", vbExclamation, "Ligne dupliquée"
        Case Else
            'Toute autre raison
            MsgBox "Attention : " & vbCrLf & "Veuillez d'abord compléter la ligne avant de passer à une autre ou d'effectuer une autre opération.", vbExclamation, "Action non conventionnelle"
    End Select
    Response = acDataErrContinue
End Sub

XIV-A-7. La fonction publique DataFormCompleted()

 Cette fonction permet de vérifier qu'un formulaire simple est correctement alimenté, sous-entendu, qu'il ne manque pas une valeur dans un des champs.

Cette fonction parcourt tous les champs du formulaire afin d'en vérifier la nullité.

Pour ce faire, on utilise un tableau de Strings alimenté avec la collection de champs et on affecte chacun d'eux à une variable objet de type Control.

Rappel
Tous les champs des formulaires simples voient leur propriété Tag définie à Null ou NotNull  :

  • Null  : la valeur vide ou nulle est autorisée :
  • NotNull  : la valeur vide ou nulle est interdite.


Il est vrai que l'on aurait pu exploiter la propriété Null Interdit issue de la table, mais je pars du principe que pour ce projet, c'était démesuré et cela aurait nécessité du code un peu plus complexe pour finalement, aboutir au même résultat.

Illustration de la propriété :

Image non disponible

Une erreur est levée à partir du moment où la nullité est rencontrée…

  • Si tout va bien que l'analyse aboutit, cette procédure appelle FillDataChecker afin d'enrichir le tableau g_intCompletionIndex des étapes.
  • Si elle échoue, sous-entendu qu'il manque une valeur dans un des champs, la fonction retourne un MsgBox où la question autorise malgré tout à continuer (opération voulue expressément dans le contexte de ce projet puisque l'on considère que l'opérateur peut très bien ne pas connaître l'information manquante).
DataFormCompleted
Sélectionnez
Public Function DataFormCompleted(ByRef TargetForm As Form, ByVal AddNewRecord As Boolean, ByVal FieldControlCollection As String, ByVal NextFormID As Integer, Optional ByVal IsLastStep As Boolean = False) As VbMsgBoxResult
'---------------------------------------------------------------------------
' Function      : DataFormCompleted
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Vérifie que les données du formulaire sont complétées
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 AddNewRecord : True si mode ajout
'                 FieldControlCollection : Liste des champs utilisés
'                 NextFormID : ID du formulaire suivant

' Return Codes  : VbMsgBoxResult = Réponse au Msgbox
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Const ADD_MESSAGE                                      As String = "Avant d'ajouter un nouvel enregistrement, vous devez faire en sorte que l'enregistrement en cours soit complété."
Const ERR_MESSAGE                                      As String = "Avant de changer d'écran, vous devez faire en sorte que ce formulaire possède au moins un enregistrement représentant un site."
Const IGNORE_MESSAGE                                   As String = vbCrLf & vbCrLf & "Voulez-vous ignorer ce message et continuer ?"
Dim strMessage                                         As String
Dim straFieldControlCollection()                       As String
Dim D                                                  As Integer
Dim oControl                                           As Control

    On Error GoTo L_ErrDataFormCompleted
    'On affecte le bon message selon le mode
    If AddNewRecord Then
        strMessage = ADD_MESSAGE & IGNORE_MESSAGE
    Else
        strMessage = ERR_MESSAGE & IGNORE_MESSAGE
    End If
    'Affectation du tableau de champs
    straFieldControlCollection = Split(FieldControlCollection, ";")
    'Pour chacun des champs
    For D = LBound(straFieldControlCollection) To UBound(straFieldControlCollection)
        'On affecte l'objet au contrôle du formulaire
        Set oControl = TargetForm.Controls(straFieldControlCollection(D))
        With oControl
            'S'il est nul
            If IsNull(.Value) Then
                'Si la propriété Remarque spécifie l'interdiction du Null
                If .Tag = "NotNull" Then
                    'On lève une erreur
                    Err.Raise 94, "Donnée requise", strMessage
                End If
            End If
        End With
    Next
    'On enrichit le tableau récapitulatif de vérification
    If IsLastStep Then
        'Si c'est la dernière, on prend l'index de la constante
        Call FillDataChecker(MAX_STEP_WIZ, True)
    Else
        'Celle en cours
        Call FillDataChecker(NextFormID - 1, True)
    End If
    DataFormCompleted = True

    On Error GoTo 0
L_ExDataFormCompleted:
    Set oControl = Nothing
    Exit Function

L_ErrDataFormCompleted:
    'En cas d'erreur (optimiste, on vérifie les data), la fonction retourne un MsgBox
    DataFormCompleted = MsgBox(Err.Description, vbExclamation + vbYesNo, Err.Source)
    'On décrémente le tableau récapitulatif de vérification pour cette étape
    If Not IsLastStep Then
        Call FillDataChecker(NextFormID - 1, False)
    End If
    Resume L_ExDataFormCompleted
End Function

XIV-A-8. La fonction publique DataSubFormCompleted()

Cette fonction permet de vérifier qu'un sous-formulaire est correctement alimenté, sous-entendu, qu'il ne manque pas une valeur dans un des champs.

Elle appelle la fonction privée VerifyFormDataTable() afin de lire chaque enregistrement, champ par champ.

  • Si tout va bien que l'analyse aboutit, cette procédure appelle FillDataChecker afin d'enrichir le tableau g_intCompletionIndex des étapes.
  • Si elle échoue, sous-entendu qu'il manque une valeur quelque part, la fonction retourne un MsgBox où la question autorise malgré tout à continuer (opération voulue expressément dans le contexte de ce projet puisque l'on considère que l'opérateur peut très bien ne pas connaître l'information manquante).
DataSubFormCompleted
Sélectionnez
Public Function DataSubFormCompleted(ByVal TargetTable As String, ByVal FieldCollection As String, ByVal AddNewRecord As Boolean, ByVal NextFormID As Integer, Optional ByVal IsLastStep As Boolean = False) As VbMsgBoxResult
'---------------------------------------------------------------------------
' Function      : DataSubFormCompleted
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Vérifie que les données du sous-formulaire sont complétées
'...........................................................................
' Parameters    : TargetTable : Table source
'                 FieldControlCollection : Liste des champs utilisés
'                 AddNewRecord : True si mode ajout
'                 NextFormID : ID du formulaire suivant

' Return Codes  : VbMsgBoxResult = Réponse au Msgbox
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Const ADD_MESSAGE                                      As String = "Avant d'ajouter un nouvel enregistrement, vous devez faire en sorte que l'enregistrement en cours soit complété."
Const ERR_MESSAGE                                      As String = "Avant de changer d'écran, vous devez faire en sorte que : " & vbCrLf & " - que l'enregistrement en cours soit complété." & vbCrLf & " - le formulaire possède au moins un enregistrement."
Const IGNORE_MESSAGE                                   As String = vbCrLf & vbCrLf & "Voulez-vous ignorer ce message et continuer ?"
Dim strMessage                                         As String
Dim blnAddNew                                          As Boolean

    On Error GoTo L_ErrDataSubFormCompleted
    'On affecte le bon message selon le mode
    If AddNewRecord Then
        strMessage = ADD_MESSAGE & IGNORE_MESSAGE
    Else
        strMessage = ERR_MESSAGE & IGNORE_MESSAGE
    End If
    'On appelle la fonction de vérification des données dans la table
    If VerifyFormDataTable(TargetTable, FieldCollection) = False Then
        Err.Raise 94, "Données requises", strMessage
    Else
        'On enrichit le tableau récapitulatif de vérification
        If IsLastStep Then
            'Si c'est la dernière, on prend l'index de la constante
            Call FillDataChecker(MAX_STEP_WIZ, True)
        Else
            'Celle en cours
            Call FillDataChecker(NextFormID - 1, True)
        End If
    End If
    On Error GoTo 0
L_ExDataSubFormCompleted:
    Exit Function

L_ErrDataSubFormCompleted:
    'En cas d'erreur (optimiste, on vérifie les data), la fonction retourne un MsgBox
    DataSubFormCompleted = MsgBox(Err.Description, vbExclamation + vbYesNo, Err.Source)
    'On décrémente le tableau récapitulatif de vérification pour cette étape
    If NextFormID <> LAST_FORM_INDEX Then
        Call FillDataChecker(NextFormID - 1, False)
    End If
    Resume L_ExDataSubFormCompleted
End Function

XIV-A-9. La fonction privée VerifyFormDataTable()

Cette fonction permet de vérifier la complétude de la table source et la collection de champs de cette table utilisée dans les sous-formulaires (certes, on aurait pu parcourir cette collection via l'objet Fields, mais ce n'est pas nécessaire ici).

VerifyFormDataTable
Sélectionnez
Private Function VerifyFormDataTable(ByVal TargetTable As String, ByVal FieldCollection As String) As Boolean
'---------------------------------------------------------------------------
' Function      : VerifyFormDataTable
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Contrôle champ par champ la nullité du contenu
'...........................................................................
' Parameters    : TargetTable : Table ou requête source
'                 FieldCollection : Champs de la source

' Return Codes  : Boolean = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim SQLSelect                                          As String
Dim oRS                                                As DAO.Recordset
Dim straFields()                                       As String
Dim strFieldName                                       As String
Dim vntValue                                           As Variant
Dim F                                                  As Integer
Dim lngNBRecords                                       As Long
Dim lngRowCount                                        As Long
Dim blnDataMissing                                     As Boolean

    On Error GoTo L_ErrVerifyFormDataTable
    blnDataMissing = False
    'Clause SQL sélectionnant tous les enregistrements de la table
    SQLSelect = "SELECT * FROM " & TargetTable & ";"
    'Ouverture du Recordset
    Set oRS = CurrentDb.OpenRecordset(SQLSelect, 2)
    'Affectation du tableau de champs
    straFields = Split(FieldCollection, ";")
    With oRS
        If Not .EOF Then
            .MoveLast
            'On compte le nombre d'enregistrements
            lngNBRecords = .RecordCount
            .MoveFirst
            Do While Not .EOF
                lngRowCount = lngRowCount + 1
                'Pour chacun des champs
                For F = LBound(straFields) To UBound(straFields)
                    'On attribue son nom à la variable
                    strFieldName = straFields(F)
                    'On verifie s'il est null
                    If IsNull(.Fields(strFieldName)) Then
                        'Auquel cas, on sort en attribuant True à la variable
                        blnDataMissing = True
                        Exit For
                    End If
                Next
                'À partir du moment  la variable est True, il est inutile de continuer, il y a une omission.
                If blnDataMissing Then
                    Exit Do
                End If
                .MoveNext
            Loop
        Else
            'S'il n'y a aucun enregistrement
            Err.Raise 3021
        End If
         'On ferme le Recordset
        .Close
    End With
    'La fonction retourne la négation logique de la valeur de la variable
    VerifyFormDataTable = Not blnDataMissing
    On Error GoTo 0
L_ExVerifyFormDataTable:
    'On libère l'objet
    Set oRS = Nothing
    Exit Function

L_ErrVerifyFormDataTable:
    VerifyFormDataTable = False
    Resume L_ExVerifyFormDataTable
End Function

XIV-A-10. La procédure publique AddNewOrCancel

Cette procédure permet d'ajouter ou d'annuler un enregistrement selon l'intitulé du bouton passé en paramètre par le biais de l'objet Form.

On lui passe l'index du sous-formulaire, le nom de la table source, la collection de champs de cette table, le nom du premier contrôle devant être focalisé, la valeur de l'ID principal (ici celui du site), la valeur de l'index du prochain formulaire et la valeur permettant de savoir si on arrive à la dernière étape…

  • Si le bouton porte l'intitulé Ajouter :

    • s'il s'agit d'un sous-formulaire, on ajoute une ligne vide via la méthode GoToRecord avec le paramètre acNewRec de l'objet DoCmd ;
    • s'il s'agit d'un formulaire simple, on appelle la fonction DataFormCompleted() afin d'empêcher l'ajout s'il n'est pas complété.
  • Si le bouton porte l'intitulé Annuler, on invoque un simple RunCommand avec le paramètre acCmdUndo de l'objet DoCmd.
AddNewOrCancel
Sélectionnez
Public Sub AddNewOrCancel(ByRef TargetForm As Form, ByVal SubFormIndex As Integer, ByVal TableSource As String, ByVal FieldCollection As String, ByVal FirstControl As String, ByVal SharedForeignIdentity As Boolean, ByVal NextFormID As Integer, Optional ByVal IsLastStep As Boolean = False)
'---------------------------------------------------------------------------
' Sub       : AddNewOrCancel
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Double procédure permettant d'ajouter un nouvel enregistrement ou d'annuler celui en cours de saisie
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 SubFormIndex : Si <> 0 signifie qu'il y a un sous-formulaire
'                 TableSource : Table ou requête source
'                 FieldCollection : Liste des champs de la source
'                 FirstControl : Nom du 1er contrôle
'                 SharedForeignIdentity : True si la source contient une valeur primaire commune (ici NoSite)
'                 NextFormID = ID du formulaire suivant

' Return Codes  :  None
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim oSubForm                                           As Form
    With TargetForm
        'Si la légende est {Annuler}
        Select Case .cmdAdd.Caption
            Case CMD_CANCEL
                'On demande confirmation de l'annulation
                If MsgBox("Voulez-vous annuler l'ajout en cours ?", vbQuestion + vbYesNo, "Annuler") = vbYes Then
                    'On pose une gestion d'erreurs optimiste
                    On Error Resume Next
                    'On appelle la méthode RunCommand de la constante AcCommand permettant l'annulation en cours
                    DoCmd.RunCommand acCmdUndo
                    'On réinitialise le gestionnaire d'erreurs
                    On Error GoTo 0
                    'On change l'intitulé des boutons
                    .cmdAdd.Caption = CMD_ADD
                    .cmdDelete.Caption = CMD_DELETE
                End If
                'Si la légende est {Ajouter}
            Case CMD_ADD
                'On met à jour les données sous-jacentes du formulaire en actualisant les modifications apportées aux enregistrements
                TargetForm.Requery
                'S'il y existe un index, on affecte l'objet oSubForm au sous-formulaire
                '(SubFormIndex=0 s'il n'y a pas de sous-formulaire)
                If SubFormIndex Then
                    'On affecte l'objet à ce sous-formulaire
                    Set oSubForm = Forms(FORM_DATA_WIZ & SubFormIndex)
                    With oSubForm
                        'On donne le focus au sous-formulaire
                        .SetFocus
                        On Error Resume Next
                        DoCmd.GoToRecord , , acNewRec
                        'Si la table contient le  du site...
                        If SharedForeignIdentity Then
                            'On affecte au champ (ici : [NoSite]), la valeur de la variable publique
                            'qui contient sa valeur en cours.
                            .Form.NoSite = g_strNoSiteEnCours
                        End If
                        'On donne le focus au premier contrôle disponible
                        .Form.Controls(FirstControl).SetFocus
                        'On réinitialise le gestionnaire d'erreurs
                        On Error GoTo 0
                    End With
                Else
                    'On appelle la fonction interne DataFormCompleted() afin de vérifier si l'ajout est possible
                    If DataFormCompleted(TargetForm, False, FieldCollection, NextFormID, IsLastStep) = vbNo Then Exit Sub
                End If
                'On change l'intitulé des boutons
                .cmdAdd.Caption = CMD_CANCEL
                .cmdDelete.Caption = CMD_SAVE
        End Select
    End With
    'On libère l'objet...
    Set oSubForm = Nothing
End Sub

XIV-A-11. La procédure publique DeleteOrSaveRecord

Cette procédure permet de supprimer ou de sauvegarder un enregistrement selon l'intitulé du bouton passé en paramètre par le biais de l'objet Form.

On lui passe l'index du sous-formulaire, la valeur cible (ID), la clause SQL de suppression et le sujet illustrant le contexte.

  • Si le bouton porte l'intitulé Supprimer, on invoque un Excecute de l'objet CurrentDB.
  • Si le bouton porte l'intitulé Sauvegarder, on invoque un simple Requery.
DeleteOrSaveRecord
Sélectionnez
Public Sub DeleteOrSaveRecord(ByRef TargetForm As Form, ByVal SubFormIndex As Integer, ByVal TargetIDValue As Variant, ByVal DeleteClause As String, ByVal Subject As String)
'---------------------------------------------------------------------------
' Sub       : DeleteOrSaveRecord
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Double procédure permettant d'effacer un enregistrement ou de le sauvegarder
'...........................................................................
' Parameters    : TargetForm : Formulaire cible
'                 SubFormIndex : Si <> 0, on agit sur le sous-formulaire
'                 TargetIDValue : Valeur de l'identifiant
'                 DeleteClause : Clause SQL DELETE FROM
'                 Subject : Sujet de l'élément à supprimer

' Return Codes  :  = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim oSubForm                                           As Form

    On Error GoTo L_ErrDeleteOrSaveRecord

    'S'il y existe un index (SubFormIndex=0 s'il n'y a pas de sous-formulaire)
    If SubFormIndex Then
        'On affecte l'objet à ce sous-formulaire
        Set oSubForm = TargetForm.Form(FORM_DATA_WIZ & SubFormIndex)
    End If

    Select Case TargetForm.cmdDelete.Caption
            'Si la légende est {Supprimer}
        Case CMD_DELETE
            'Et si l'identifiant n'est pas nul
            If Not IsNull(TargetIDValue) Then
                'On confirme la suppression
                If MsgBox("Êtes-vous certain de vouloir supprimer " & Subject & " ?" & vbCrLf & vbCrLf & "Cette action est irréversible.", 292, "Supprimer un employé") = 6 Then
                    'On exécute la requête DELETE
                    CurrentDb.Execute DeleteClause, dbFailOnError
                    'Si c'est le sous-formulaire
                    If SubFormIndex Then
                        oSubForm.Requery
                    Else
                        TargetForm.Requery
                    End If
                End If
            Else
                'Si l'identifiant est nul, on lève une erreur
                Err.Raise 94, "Pas d'identifiant", "Il n'y a aucun enregistrement sélectionné qui puisse être supprimé !"
            End If
            'On change l'intitulé des boutons
            TargetForm.cmdDelete.Caption = CMD_DELETE
            TargetForm.cmdAdd.Caption = CMD_SAVE

            'Si la légende est {Sauvegarder}
        Case CMD_SAVE
            'Si c'est le sous-formulaire
            If SubFormIndex Then
                oSubForm.Requery
            Else
                TargetForm.Requery
            End If
            'On change l'intitulé des boutons
            TargetForm.cmdAdd.Caption = CMD_ADD
            TargetForm.cmdDelete.Caption = CMD_DELETE
    End Select
    On Error GoTo 0
L_ExDeleteOrSaveRecord:
    'On libère l'objet...
    Set oSubForm = Nothing
    Exit Sub

L_ErrDeleteOrSaveRecord:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExDeleteOrSaveRecord
End Sub

XIV-A-12. La fonction publique DataTableHasRecord()

Cette fonction permet de savoir si une table ou plus exactement ici, une clause SQL fondée sur une table, contient des enregistrements.

Grâce à cette fonction, un message s'affiche en tant qu'alerte pour avertir que le formulaire doit contenir au moins un enregistrement.

DataTableHasRecord
Sélectionnez
Private Function DataTableHasRecord(ByVal SQLQuery As String) As Boolean
'---------------------------------------------------------------------------
' Function      : DataTableHasRecord
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Vérifie que la source SQLQuery possède des enregistrements
'...........................................................................
' Parameters    : SQLQuery :

' Return Codes  : Boolean = True if has data
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim oRS                                                As DAO.Recordset
    'On initialise l'objet Recordset avec la chaîne SQL passée en paramètre
    Set oRS = CurrentDb.OpenRecordset(SQLQuery, 2)
    With oRS
        'On renvoie la valeur correspondant à la position d'enregistrement actuelle :
        'False = aucun enregistrement
        DataTableHasRecord = Not .EOF
        .Close
    End With
    Set oRS = Nothing
End Function
 

XIV-A-13. La fonction publique NullData()

Cette fonction est utilisée dans tous les sous-formulaires afin de calculer la nullité des champs.

Sur le principe, elle retourne 0 si le champ est Null et 1 s'il ne l'est pas, et ce, quel que soit son type.

Remarque

Le type (DataType) ici est défini à l'image des constantes dbText (10), dbLong (4) et dbDate (8) de façon générique.

Au sein de la formule qui l'exploite dans le contrôle txtCheckdata, cette fonction est additionnée à elle-même pour établir une somme d'entiers.

Ainsi, la case à cocher chkCompleted prend la valeur True si la valeur finale de ce champ correspond à la valeur attendue.

NullData
Sélectionnez
Public Function NullData(ByVal Ctl As Variant, ByVal DataType As Integer) As Integer
'---------------------------------------------------------------------------
' Function      : NullData
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Retourne 1 ou 0 selon la nullité du contenu
'...........................................................................
' Parameters    : Ctl : contrôle de formulaire cible
'                 DataType : Type de donnée du contrôle

' Return Codes  : Integer = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------

    On Error GoTo L_ErrNullData

    NullData = 0
    If Not IsNull(Ctl) Then
        NullData = 1
    End If
    Select Case DataType
        Case 10
            If Len(Nz(Ctl, "")) Then
                NullData = 1
            End If
        Case 4
            If (Ctl > 0) Then
                NullData = 1
            ElseIf (Ctl = 0) And (Len(Ctl) < 2) Then
                NullData = 0
            Else
                NullData = Nz(Ctl, 0)
            End If
        Case 8
            If IsDate(Ctl) Then
                NullData = 1
            End If
        Case Else
    End Select

    On Error GoTo 0
L_ExNullData:
    Exit Function

L_ErrNullData:
    NullData = 0
    Resume L_ExNullData
End Function

XIV-A-14. La fonction événementielle publique NotInList()

Cette fonction est une réplique générique associée à la rédaction typique de l'événement NotInList() où l'on force le paramètre Response à acDataErrContinue accompagné d'un message personnalisé sans pour autant permettre un ajout en cas de valeur manquante, car cela ne s'y prête pas.

Le message est construit dynamiquement avec le contenu du paramètre ItemSubject.

Vous remarquerez d'ailleurs que l'on passe ici le paramètre NewData et que la valeur Response est la valeur de retour.

NotInList
Sélectionnez
Public Function NotInList(ByVal NewData As String, ByVal ItemSubject As String) As Integer
'---------------------------------------------------------------------------
' Function      : NotInList
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Fonction générique de substitution de l'événement NotInList d'un ComboBox
'...........................................................................
' Parameters    : NewData : Valeur que l'utilisateur a entrée dans la zone de liste modifiable.
'                 ItemSubject : Sujet de la liste déroulante

' Return Codes  : Integer = Valeur la fonction NotInList
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
    MsgBox ItemSubject & " '" & NewData & "' ne fait pas partie de la liste !", vbExclamation, "Choix incorrect"
    NotInList = acDataErrContinue
End Function

XIV-B. VII-A. Le Module basMain

XIV-B-1. L'en-tête de la feuille

L'en-tête ici ne comporte pas un grand jeu de constantes, quelques variables et une énumération.

  • Les constantes de toutes catégories concernent l'affichage des contrôles masqués, de la définition des étapes et d'index de formulaire, des noms génériques des formulaires, des légendes des boutons et des couleurs des intitulés.
  • L'énumération définit les valeurs de direction de la navigation pour les boutons appelant la méthode OpenTheForm avec le paramètre précédent ou suivant
  • Les variables publiques * sont commentées dans le code.

* On n'utilise habituellement pas et par préférence ce type de variable, mais pour un projet de cette envergure et qualifié de jetable dans le jargon des professions en rapport, cela n'a pas grande importance.

basMain
Sélectionnez
'**********************************************************************
' Module            : basMain
' Type              : Module
' DateTime          : 30/07/2015
' Author            : Argyronet
' Review            : 23/09/2015 - Jean-Philippe AMBROSINO
' Review date       : 27/11/2015
' Purpose           : Tutoriel : Réalisez un assistant de présaisie des données pour alimenter la base d'un serveur central
'
'**********************************************************************
Option Compare Database
Option Explicit

'Affichage ou masquage des contrôles : Prod = False ; Test = True
Public Const SHOW_HIDDEN_CONTROLS                      As Boolean = False
'Constantes de données
Public Const MAX_STEP_WIZ                              As Integer = 5
Public Const LAST_FORM_INDEX                           As Integer = 99
'Constantes des noms de formulaire
Public Const FORM_DATA_WIZ                             As String = "frmDataWizard"
Public Const SUBFORM_DATA_WIZ                          As String = "sfrmDataWizard"
Public Const SUB_FORM_SUFFIX                           As String = "_sub"
Public Const FORM_START                                As String = "frmStart"
Public Const FORM_END                                  As String = "frmEnd"
'Constantes des boutons de commandes
Public Const CMD_STOP                                  As String = "&Arrêter"
Public Const CMD_CLOSE                                 As String = "&Fermer"
Public Const CMD_ADD                                   As String = "Aj&outer"
Public Const CMD_SAVE                                  As String = "S&auvegarder"
Public Const CMD_VALIDATE                              As String = "V&alider"
Public Const CMD_MODIFY                                As String = "M&odifier"
Public Const CMD_CANCEL                                As String = "&Annuler"
Public Const CMD_DELETE                                As String = "S&upprimer"
Public Const CMD_CANCEL_3DOTS                          As String = CMD_CANCEL & "..."
Public Const CMD_RESTART                               As String = "&Recommencer..."

Public Const CLR_REDCOLOR                              As Long = 255
Public Const CLR_GREENCOLOR                            As Long = 39168

'Énumération qui définit le sens d'ouverture des formulaires
Public Enum ge_FormDirection
    GotoNext = 1
    GotoPrevious = 3
End Enum


'Variables publiques :
'No du site et nom du domaine exploités par la session en cours
Public g_strNoSiteEnCours                              As String
Public g_strDomaineSiteEnCours                         As String
'Liste des étapes incomplètes
Public g_strDataSteps                                   As String
'Tableau des  d'étapes complétées ou non
Public g_intCompletionIndex(1 To MAX_STEP_WIZ)         As Integer

XIV-B-2. La procédure publique FillDataChecker

Cette procédure permet d'alimenter le tableau d'entiers avec une valeur True ou False à l'Index selon la valeur de Completed.

FillDataChecker
Sélectionnez
Public Sub FillDataChecker(ByVal Index As Integer, ByVal Completed As Boolean)
'---------------------------------------------------------------------------
' Sub       : FillDataChecker
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Alimente le tableau g_intCompletionIndex selon l'index passé
'...........................................................................
' Parameters    : Index : Index du tableau
'                 Completed : True si complété

' Return Codes  :  None
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
    g_intCompletionIndex(Index) = Completed
End Sub

XIV-B-3. La fonction publique AllStepsAreCompleted()

Cette fonction permet de vérifier les étapes qui sont complétées ou non et d'en établir une liste qui est fournie à la variable g_strDataPage.

AllStepsAreCompleted
Sélectionnez
Public Function AllStepsAreCompleted(ByRef Count As Integer) As Boolean
'---------------------------------------------------------------------------
' Function      : AllStepsAreCompleted
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Vérifie les étapes complétées
'...........................................................................
' Parameters    : Count : Valeur représentant le nombre d'étapes

' Return Codes  : Boolean = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim P                                                  As Integer
Dim intStepCount                                       As Integer
Dim strDataPage                                        As String

    intStepCount = 0
    strDataPage = ""
    'Pour chacune des étapes
    For P = LBound(g_intCompletionIndex) To UBound(g_intCompletionIndex)
        'Si elle a été affectée à True via FillDataChecker()
        If (g_intCompletionIndex(P) = True) Then
            'On incrémente le nombre d'étapes valides
            intStepCount = intStepCount + 1
        Else
            'Sinon, on remplit une chaîne stipulant les étapes ignorées
            strDataPage = strDataPage & " - étape  " & P & vbCrLf
        End If
    Next
    Count = intStepCount
    'La fonction est True si le nombre d'étapes complétées est égal au nombre total d'étapes
    AllStepsAreCompleted = (Count = MAX_STEP_WIZ)
    'Définit le message final
    If Len(strDataPage) Then
        g_strDataPage = "Liste des étapes incomplètes :" & vbCrLf & vbCrLf & strDataPage
    Else
        g_strDataPage = "Liste des étapes incomplètes :" & vbCrLf & vbCrLf & "Aucune"
    End If
End Function

XIV-B-4. La fonction publique EraseAllDataBefore()

Cette fonction permet la remise à blanc des tables de données utilisateur.

Elle est appelée au début du lancement lorsque la case à cocher sur l'écran d'accueil est cochée.

EraseAllDataBefore
Sélectionnez
Public Function EraseAllDataBefore(ByRef ErrDescription As String) As Boolean
'---------------------------------------------------------------------------
' Function      : EraseAllDataBefore
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Fonction de RAZ des données occultant certaines tables
'...........................................................................
' Parameters    : ErrDescription = Message d'erreur

' Return Codes  : Boolean = True if success
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Const TABLE_TO_IGNORE                                  As String = "tbl_ListeDepartements;tbl_ListeDesCommunes;tbl_ListeDesRegions;tbl_Privileges;tbl_TypesContrat"
Const CHAR_SEP                                         As String = ";"
Const DELETE_DATA                                      As String = "DELETE * FROM #T;"
Dim oDB                                                As DAO.Database
Dim strListOfTables                                    As String
Dim straTables()                                       As String
Dim SQLDelete                                          As String
Dim T                                                  As Integer
    On Error GoTo L_ErrEraseAllDataBefore
    strListOfTables = GetListOfTables(TABLE_TO_IGNORE, CHAR_SEP)
    Set oDB = CurrentDb
    With oDB
        Debug.Print strListOfTables
        straTables = Split(strListOfTables, CHAR_SEP)
        For T = LBound(straTables) To UBound(straTables)
            SQLDelete = Replace(DELETE_DATA, "#T", straTables(T))
            .Execute SQLDelete, dbFailOnError
        Next
        .Close
    End With
    EraseAllDataBefore = True
    On Error GoTo 0
L_ExEraseAllDataBefore:
    Set oDB = Nothing
    Exit Function

L_ErrEraseAllDataBefore:
    EraseAllDataBefore = False
    'Message d'erreur avec 
    ErrDescription = Err.Description & " - (" & Err.Number & ")"
    Resume L_ExEraseAllDataBefore
End Function

XIV-B-5. La fonction privée GetListOfTables()

Cette fonction établit la liste des tables devant être remises à blanc.

Elle est appelée par la fonction EraseAllDataBefore() ci-avant.

EraseAllDataBefore
Sélectionnez
Private Function GetListOfTables(ByVal ExcludeTables As String, Optional ByVal CharSep As String = ";") As String
'---------------------------------------------------------------------------
' Function      : GetListOfTables
' DateTime      : 27/11/2015
' Author        : Jean-Philippe AMBROSINO : Argyronet sur developpez.com (Code du tutoriel 11-2015)
' Purpose       : Retourne la liste des tables devant être vidées
'...........................................................................
' Parameters    : ExcludeTables : Liste des tables à exclure

' Return Codes  : String = Liste des tables
'...........................................................................
' Notice        :
'---------------------------------------------------------------------------
Dim oDB                                                As DAO.Database
Dim oTDF                                               As DAO.TableDef
Dim strDataTables                                      As String
Dim straExcludeTables()                                As String
Dim strExcludeTable                                    As String
Dim T                                                  As Integer
    Set oDB = CurrentDb
    straExcludeTables = Split(ExcludeTables, CharSep)
    'Énumère toutes les tables sauf les tables système
    For Each oTDF In oDB.TableDefs
        With oTDF
            If .Attributes = 0 And Left$(.Name, 4) <> "MSys" Then
                'On incrémente la chaîne avec le nom des tables
                strDataTables = strDataTables & Trim$(.Name) & CharSep
            End If
        End With
    Next
    'On ferme la base instanciée
    oDB.Close
    Set oDB = Nothing
    'On parcourt le tableau des tables à exclure
    For T = LBound(straExcludeTables) To UBound(straExcludeTables)
        strExcludeTable = straExcludeTables(T)
        'On remplace chaque table à exclure et son ; par une chaîne vide
        strDataTables = Replace(strDataTables, strExcludeTable & CharSep, "")
    Next
    'On élimine le dernier caractère et on affecte le tout comme valeur de retour
    If Right(strDataTables, 1) = CharSep Then
        GetListOfTables = Left$(strDataTables, Len(strDataTables) - 1)
    End If
End Function

XV. Mise en exécution

Lorsque vous pensez que vous avez pu mettre en œuvre ce projet en l'état, il doit être prêt à tourner.

Vous avez le choix de lancer indifféremment :

  • le formulaire frmStart ;
  • la macro AutoExec ;
  • la procédure de démarrage que vous auriez créé (non présente dans le code, ici) ;
  • La fermeture d'Access et ouvrir l'application directement via un raccourci, par exemple.

Le premier formulaire s'affiche et vous pouvez commencer les tests de saisie et de comportement pour voir si vous n'avez rien omis ou bien si l'interfaçage vous satisfait ; vous pouvez alors selon vos constats, améliorer ou modifier des éléments que vous jugeriez nécessaires.

XV-A. En images, l'exécution peut se dérouler ainsi

L'écran d'accueil est affiché et on souhaite remettre à blanc les données puisque la case cochée…

Image non disponible

Un message de confirmation est alors affiché :

  • si vous cliquez sur Oui toutes les données sont effacées de façon irréversible et vous passez à l'étape N° 1 ;
  • si vous cliquez sur Non, vous passez à l'étape N° 1 sans effacer les données ;
  • si vous cliquez sur Annuler, vous restez sur cet écran.
Image non disponible

À l'étape N° 1, on définit le nom du site.

Dans ce projet, il est considéré (comme cela l'est stipulé dans le texte en rouge) que le site est unique et qu'il reste en référence dans toutes les tables sous-jacentes.

Image non disponible

Si toutefois vous tentez de passer à l'étape suivante alors que le formulaire n'est pas du tout rempli, le message suivant fait son apparition.

Image non disponible

Comme je vous l'ai déjà précisé, les messages d'erreurs sont qualifiés d'optimistes.

Dans l'absolu, pour cette première étape, on aurait dû rendre le message non permissif et interdire la non-saisie du site, puisque c'est le site qui fait office de référence.

Mais il est considéré ici que c'est juste pour la démonstration et que vous pouvez modifier le comportement du message en conséquence de l'appel source.

Si toutefois vous vouliez modifier le message, il faudrait remplacer les boutons vbYesNo (Oui - Non) par le bouton OK et interdire le passage au formulaire suivant.

De là plusieurs options s'offrent à vous :

  • griser le bouton suivant tant que les données ne sont pas remplies ;
  • modifier le message de telle sorte qu'il affiche un bouton OK.

Une fois que vous avez rempli toutes les données de l'étape N° 1 vous passez à l'étape N° 2 pour alimenter l'ensemble du personnel du site.

Le code inscrit dans ce formulaire permet de transformer les prénoms en Nom Propre et les noms de famille en MAJUSCULES.

Par ailleurs, l'adresse e-mail est composée automatiquement sur le principe déjà expliqué dans les pages précédentes et notamment ici.

Vous ne pouvez pas remarquer ici l'effet clignotant de l'étiquette mentionnant que la ligne est complétée puisque c'est une image. Mais dans la vidéo un peu plus bas, cet effet est tout à fait visible.

Image non disponible

L'étape suivante N° 3 va vous permettre de saisir les communes exploitées par le site.

Le principe est simple, vous choisissez le département et aussitôt la liste de droite se filtre sur les communes de ce dernier. Il suffit alors de choisir la bonne commune.

Vous pouvez remarquer qu'en cours de saisie, la mention ligne incomplète est affichée tout simplement parce que l'opérateur est en train de la remplir.

Image non disponible

Si à ce stade il tente de passer à l'étape suivante, le message d'alerte fait son apparition en lui proposant d'ignorer ou de continuer.

Image non disponible

Il propose alors :

  • soit de finir la saisie (pas le choix, et toc !) ;
  • soit d'annuler la saisie en cours…

À l'étape N° 4, le formulaire propose de saisir un ou plusieurs contrats.

Tous les champs ici sont obligatoires.

Si toutefois il existe plusieurs contrats déjà enregistrés, le bouton permettant d'en afficher la liste est disponible.

Image non disponible

L'avant-dernière étape N° 5 est dédiée à l'alimentation des communes affectées à certains contrats.

Dans la liste de gauche figurent les contrats que vous avez déjà enregistrés, et dans la liste de droite, les communes supervisées par le site.

Attention

Il n'a pas été prévu ici de code inhérent au comportement des données s'il manque des informations (site, contrat, etc.).

Image non disponible

Dans la mesure où vous avez complété toutes les étapes, le dernier écran vous spécifie qu'aucune étape n'a été omise…

Il décrit alors au sein de ce formulaire la marche à suivre.

Image non disponible

Ci-dessous, par exemple, on peut constater que les étapes N° 1 et N° 2 sont incomplètes.

Image non disponible

XVI. Mise en pratique du projet

Ainsi que cela déjà été élaboré au début de ce tutoriel, l'objectif de ce projet est de fournir une application prête à l'emploi dont l'objectif est de collecter l'ensemble des données que vous souhaitez centraliser dans une seule et même base de données, et ce, quel que soit son type.

Il est sous-entendu finalement qu'une autre application est censée être en place sur ces sites pour pouvoir interagir avec les données que les destinataires ou responsables de sites ont saisies.

Dans le contexte professionnel dans lequel je travaille, l'ensemble des sites possède une application itinérante qui est identique à l'application centrale où les utilisateurs, par le biais de processus d'export, déversent leurs données dans l'application centrale.

Mais pour ce faire, il est impératif que l'application itinérante soit pourvue des données en rapport avec les communes qu'ils exploitent d'une part, et les différents techniciens appartenant au site, d'autre part.

Lorsqu'ils font leur export, les données se croisent avec les informations récoltées.

J'ajoute à ce tutoriel une petite vidéo qui va vous permettre de vous rendre compte du mode de fonctionnement de cette application :


Démonstration du tutoriel en mode utilisation


Dans le jeu de tests présentés ci-dessus, j'ai expressément omis les informations pour afficher les messages d'erreur et pour arriver finalement avec le compte rendu final des étapes qualifiées d'incomplètes.

Je suis alors revenu au formulaire précédent jusqu'à l'étape incriminée et j'ai corrigé mon formulaire afin de n'avoir aucune erreur.

XVII. Conclusion

Bien que ce tutoriel reste axé sur un cas d'école très particulier, il reste intéressant d'en examiner le contenu, ne serait-ce que pour apprendre à éviter la redondance de code au sein des formulaires, surtout lorsque ceux-ci font la même chose.

Vous avez pu voir comment il était aisé de centraliser les procédures événementielles en un seul et même bloc et surtout comment il était plus souple de passer des paramètres pour pouvoir faire en sorte que ces procédures exécutent ce que vous souhaitez faire. Vous avez pu également apprendre à rendre optimiste une gestion d'erreurs, mais aussi à construire des classes formulaires génériques où seul le changement des valeurs de constantes suffit à faire marcher l'interface.

Pour pouvoir partager ce tutoriel et le transposer à l'image de ce que vous pourriez en attendre, j'ai dû apporter pas mal de modifications et adapter le code pour qu'il soit à la fois simplifié et compréhensible par rapport au projet original. Il n'en est pas moins que son élaboration reste assez délicate si l'on considère qu'il ne faut rien omettre, tant au sein des formulaires que de leurs propriétés.

Vous voudrez bien noter que toutes suggestions d'amélioration dans le but de parfaire ce projet restent bien évidemment les bienvenues.

XVIII. Remerciements

Je tiens à remercier tout particulièrement toutes celles et tous ceux qui ont participé à la relecture de ce document en y incluant leurs remarques.

- Claude Leloup pour la relecture orthographique.

- Djibril pour la partie technique de l'édition.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Jean-Philippe AMBROSINO. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.