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

Jean-Philippe AMBROSINO

Commentez ♪

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

- Auteur : Jean-Philippe AMBROSINO

- Parution : 3 novembre 2015

- Mise à jour : 2 mars 2016

- Public visé :

- Licence : 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.

Sommaire

- I. Avant-propos

- I-A. Niveau

- I-B. Contact

- I-C. Le projet proposé

- I-D. Contexte métier

- II. Pourquoi ce projet

- II-A. Contexte

- II-B. De quoi avez-vous besoin ?

- III. Présentation du projet

- III-A. Structure fonctionnelle des formulaires

- IV. Les tables du projet

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

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

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

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

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

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

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

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

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

- IV-B-3. tblContrats (Les contrats)

- IV-B-4. tblPersonnel (Le personnel)

- IV-B-5. tblSites (Les sites)

- IV-C. Données des tables

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

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

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

- IV-C-4. tbl_Privileges

- V. Le schéma relationnel de l'application

- VI. Conception des formulaires

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

- VI-A-1. Le formulaire d'accueil

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

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

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

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

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

- VI-A-7. Le formulaire de fin

- VII. Description structurelle des formulaires

- VII-A. La source de données formulaires

- VII-B. Les formulaires simples

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

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

- VII-C. Les formulaires avec un sous-formulaire

- VII-C-1. Les formulaires parents

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

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

- VIII. Description détaillée des formulaires

- VIII-A. Le formulaire d'accueil

- VIII-B. Le formulaire de saisie du site

- VIII-C. Le formulaire de saisie du personnel

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

- VIII-D. Le formulaire de saisie des communes

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

- VIII-E. Le formulaire de saisie des contrats

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

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

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

- VIII-G. Le formulaire de fin

- IX. Démarrage du projet

- IX-A. Chargement par macro exemple 1

- IX-B. Chargement par macro exemple 2

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

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

- 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- XI-F-2. Les événements

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

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

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

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

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

- XI-H-2. Les événements

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

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

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

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

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

- XI-J-2. Les événements

- XI-K. Le formulaire de fin (frmEnd)

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

- XI-K-2. Les événements

- 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

- XII-A-2. Les événements

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

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

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

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

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

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

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

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

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

- XII-K. Le formulaire de fin (frmEnd)

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

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

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

- XIV. Les modules externes

- XIV-A. VII-A. Le Module basFormsFunctions

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

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

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

- XIV-A-4. La procédure OpenTheForm

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

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

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

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

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

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

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

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

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

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

- XIV-B. VII-A. Le Module basMain

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

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

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

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

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

- XV. Mise en exécution

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

- XVI. Mise en pratique du projet

- XVII. Conclusion

- XVIII. Remerciements

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.

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).

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.

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.

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.

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.

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.

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.

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.

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.

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 :

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

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

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

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

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

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

VI-A-7 - Le formulaire de fin

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 »

=(NullData([NomDuChamp1];4)+NullData([NomDuChamp2];10)+NullData([NomDuChamp3];10))

et la case à cocher associée « chkCompleted »

la case à cocher « chkCompleted »

=([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.

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.

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.

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.

N°

Champ

Formule ou Source

Description

txtTotalRows

=[frmDataWizard2_sub]![txtCountRows]

Valeur de txtCountRows*

txtNoSite

=[frmDataWizard2_sub]![txtNoSite]

Valeur de txtNoSite*

txtIDPersonne

=[frmDataWizard2_sub]![txtIDPersonne]

Valeur de txtIDPersonne*

txtNomPrenom

=[frmDataWizard2_sub]![txtNomPrenom]

Valeur de txtNomPrenom*

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).

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

=(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

=([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).

N°

Champ

Formule ou Source

Description

txtCheckdata

=NullData(de chaque champ)

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

txtCountRows

=Compte([IDPersonne])

Compte le nombre d'enregistrements

txtNoSite

NoSite

Renvoie le numéro du site en cours

txtIDPersonne

IDPersonne

Renvoie l'ID de l'employé en cours

txtNomPrenom

=[Prenom] & " " & [NomFamille]

Renvoie le nom et le prénom en cours

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.

À 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.

N°

Champ

Formule ou Source

Description

txtTotalRows

=[frmDataWizard3_sub]![txtCountRows]

Valeur de txtCountRows*

txtIDCommune

=[frmPerimeter3_sub]![txtIDCommune]

Valeur de txtIDCommune*

txtCommune

=[frmPerimeter3_sub]![txtCommune]

Valeur de txtCommune*

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).

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

=(NullData([NoSite];4)+NullData([IDCommune];4))

- la case à cocher « chkCompleted » dont la formule associée est :

Formule de chkCompleted

=([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).

N°

Champ

Formule ou Source

Description

txtCheckdata

=NullData(de chaque champ)

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

txtCountRows

=Compte([IDPersonne])

Compte le nombre d'enregistrements

txtIDCommune

IDCommune

Renvoie l'ID de la commune en cours

txtCommune

=[IDCommune].[column](1)

Valeur de txtNomCommune

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.

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.

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

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.

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

=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.

À 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.

N°

Champ

Formule ou Source

Description

txtTotalRows

=[frmDataWizard5_sub]![txtCountRows]

Valeur de txtCountRows*

txtIDContrat

=[frmPerimeter5_sub]![IDContrat]

Valeur de txtIDContrat*

txtIDCommune

=[frmPerimeter5_sub]![txtIDCommune]

Valeur de txtIDCommune*

txtNomCommune

=[frmPerimeter5_sub]![txtNomCommune]

Valeur de txtNomCommune*

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).

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

=(NullData([IDContrat];10)+NullData([IDCommune];4))

- la case à cocher « chkCompleted » dont la formule associée est :

Formule de chkCompleted

=([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).

N°

Champ

Formule ou Source

Description

txtCheckdata

=NullData(de chaque champ)

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

txtCountRows

=Compte([IDContrat])

Compte le nombre d'enregistrements

txtIDContrat

IDContrat

Renvoie l'ID du contrat en cours

txtIDCommune

IDCommune

Renvoie l'ID de la commune en cours

txtNomCommune

=[IDCommune].[column](1)

Renvoie le nom de la commune en cours

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.

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.

N°

Contrôle

Source

Description

lblStepStatus

Code

Affiche la mention de complétion selon les étapes

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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 N° 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

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

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

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

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

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

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

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

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

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 N° 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

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

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

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

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

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

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

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

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

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

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

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

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

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 N° 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 = prénom.nom@domainesite.com.

Le contrôle Prenom

Prenom_AfterUpdate

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 = prénom.nom@domainesite.com.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

'**********************************************************************
' 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

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

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

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

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

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é :

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

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

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

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 où 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

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 N° 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

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

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

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

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

'**********************************************************************
' 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 N° 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

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

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 N° " & 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

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 N°
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

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…

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.

À 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.

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.

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.

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.

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.

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.

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.).

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.

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

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 :

Animation en format "youtube" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://www.youtube.com/v/QoA8hFSrC2o

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.