Apprendre à utiliser Access pour gérer une cave à vin

Cette application de gestion de cave à vin n'a pas la prétention de rivaliser avec les logiciels existants, même gratuits, que vous pouvez facilement trouver sur la toile.
Voyez plutôt ceci comme un prétexte pour montrer combien il est facile d'utiliser Access pour réaliser des outils bien pratiques : en l'occurrence, une gestion de stock.
En effet, il s'agit de décrire différents articles (les bouteilles) que l'on entre en stock (les achats), que l'on range à un endroit précis (les casiers de la cave), que l'on sort ensuite (les consommations… avec modération) et périodiquement dresser l'inventaire (ce qui reste, valorisé ici au dernier prix d'achat).

Cet article s'adresse à des utilisateurs d'Access qui ont déjà une petite expérience du logiciel (VBA notamment) ou qui veulent se perfectionner.

Une discussion est ouverte pour vos commentaires éventuels : 4 commentaires Donner une note à l'article (5) 

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Le formulaire principal que l'on veut réaliser

Les dimensions de certaines images ont été réduites. Cliquez sur l'image pour la voir dans son format original.

Image non disponible

Les crus sont affichés par ordre alphabétique. On navigue au moyen des boutons :

Image non disponible

Image non disponible Quand l'appellation est choisie, la région s'affiche automatiquement :

Image non disponible

Image non disponible Un clic sur le bouton Image non disponible déclenche une recherche comme si vous l'aviez faite avec Google en combinant :

- le cru ;

- le millésime ;

- le propriétaire/négociant ;

- la catégorie.

Image non disponible

Image non disponible Un clic sur Image non disponible ouvre une boîte de dialogue pour rechercher l'image *.jpg à associer à cette bouteille.

Image non disponible

Image non disponible Un clic sur Image non disponible élimine définitivement l'article de la base.

Image non disponible Un clic sur le bouton Image non disponible permet de créer un nouvel enregistrement dont les caractéristiques se rapprochent de celles de la bouteille affichée. Par exemple le même cru, mais d'un autre millésime.
Après le clic, l'enregistrement nouveau est identique à celui qui a servi de modèle, l'utilisateur modifie alors ce qu'il faut et sauvegarde en cliquant sur la gouttière de sélection :

Image non disponible

Image non disponible,Image non disponible, Image non disponible Si on clique l'un des boutons Image non disponible, on peut, respectivement, enregistrer une nouvelle entrée, une nouvelle sortie ou consulter l'historique des mouvements.


Image non disponible Une série de filtres pour limiter l'affichage aux bouteilles qui correspondent aux critères choisis (on peut les conjuguer).
Nous avons donc affaire à un formulaire polyvalent (de type SCRUD : Search, Create, Read, Update, Delete) qui permet d'ajouter, de supprimer, de modifier des enregistrements et de les voir sélectivement.
On peut conjuguer plusieurs critères, par exemple : les bordeaux blancs en bouteille et dont il reste du stock :

Image non disponible

N.B. Dans toutes les zones de liste, le choix est limité aux items qui s'affichent : la recherche des bouteilles répondant au(x) critère(s) se fera par comparaison stricte du critère.
Une exception pour *Cru : la liste proposée n'est pas limitée, par exemple si l'utilisateur saisit « Corton » les crus suivants seront sélectionnés : Aloxe-Corton ; Corton Bressandes et Corton Charlemagne. (On parle alors de « comparaison étendue ».)

Image non disponible Abordons maintenant les aspects techniques de l'application.

II. Prérequis pour réaliser ceci avec Access

Ce tutoriel s'adresse à des utilisateurs qui maîtrisent déjà les bases du logiciel Access ou qui veulent faire l'effort pour progresser : l'utilisation du VBA décuple le potentiel de solutions !

Image non disponible Pour vérifier votre niveau, parcourez ces tutoriels : si vous les comprenez facilement, vous êtes OK et si ce n'est pas le cas, insistez :
- pour commencer : Maxence Hubiche Access - Les Bases ;
- pour construire des requêtes : Jean Ballat Créer des requêtes simples ;
- pour construire un formulaire : Jean-Philippe Ambrosino le chapitre 2-1-2 de Mise en surbrillance d'un enregistrement dans un formulaire ;
- pour le VBA : Olivier Lebeau Initiation au VBA Office.
Image non disponible D'une manière générale, pour vous documenter sur les propriétés d'un formulaire ou d'un état, ou de leurs contrôles :
- affichez l'objet en mode création ;
- cliquez sur la propriété, elle se met alors en surbrillance ;
- enfoncez la touche <F1>.
Pour un problème de code dans un module, placez le curseur n'importe où dans l'instruction et pressez <F1>.
L'aide Access s'ouvre alors à la bonne page.
On peut aussi :
- ouvrir l'aide <F1>, choisir l'onglet « Aide intuitive » et suivre les instructions ;
- ouvrir la fenêtre d'exécution (<Ctrl> + G), saisir un mot-clé, y placer le point d'insertion et presser <F1>.

III. Les tables et leurs relations

IV. Les bibliothèques utiles

Image non disponible

V. Réalisation du formulaire fBouteilles

V-A. La source du formulaire

On s'attend à ce que la source de fBouteilles soit la table tBouteilles.
Cela conviendrait pour un formulaire traditionnel (CRUD) qui permet de créer des enregistrements (Create), de les lire (Read), de les modifier c'est-à-dire de les mettre à jour (Update) et de les supprimer (Delete).
Nous avons voulu ici ajouter une fonctionnalité : restreindre, à la demande, le nombre des enregistrements affichés. À savoir, proposer seulement ceux qui répondent à certains critères exprimés par l'utilisateur, et cela, avec un formulaire SCRUD (Select) qui en plus, permet de sélectionner les enregistrements à afficher.

Sur DVP, on désigne souvent ce type de formulaire sous le vocable « formulaire à recherche multicritère ».

Vous trouverez une documentation abondante à cette adresse : https://access.developpez.com/cours/?page=interfaceform#interfaceform.

Ici les différents critères de sélection sont :

Image non disponible

Convention de nommage les champs s'appellent : FiltrePays, FiltreCru, […] , FiltreFormat et caStock.

Voici une méthode pour fabriquer un formulaire SCRUD basé sur une requête enregistrée.

V-A-1. Première étape : construire une requête qui affiche toutes les données qui interviennent dans le formulaire et les filtres

Le formulaire fBouteilles doit être ouvert pour que l'on puisse faire référence à son contenu.

Image non disponible

Nous prenons évidemment toutes les colonnes de tBouteilles, nous ajoutons une requête rStock pour disposer du stock et la table tRegions pour disposer de la région. La table tAppellations est aussi nécessaire pour permettre la liaison entre une bouteille et sa région.

V-A-2. Deuxième étape : ajouter les critères de sélection

Il faut dire à Access : « Si dans le formulaire la valeur du filtre est Null, alors il faut ramener tous les enregistrements. Sinon, ramener seulement ceux qui satisfont au critère contenu dans le filtre. »

V-A-2-a. Exemple pour un filtre texte à recherche étendue

Image non disponible

V-A-2-b. Exemple pour un filtre à recherche stricte

Image non disponible

V-A-2-c. Exemple pour un filtre du type groupe d'options

D'une part la requête rStock et d'autre part le groupe d'options :

Image non disponible

Le challenge, c'est de dire à Access : « Si le groupe d'options vaut :

  • 0, il faut ramener les enregistrements dont la somme des mouvements est Null ou 0 ;
  • 1, il faut ramener les enregistrements dont la somme des mouvements est différente de 0 ;
  • 2, il faut ramener tous les enregistrements. »

Nous n'avons pas trouvé la syntaxe pour exprimer le critère par rapport au nombre de bouteilles en stock, par exemple, dire « ramener les enregistrements avec stock <>0 si le groupe vaut 1 » (voyez cette discussion sur le forum à ce sujet).
C'est pour contourner cet obstacle que nous avons ajouté une colonne booléenne « OuiSiVide » à la requête rStock et nous testons ainsi :

Image non disponible

V-A-3. Troisième étape : affecter cette requête comme source du formulaire

Habituellement, il suffit de renseigner la requête que l'on vient de construire comme source du formulaire :

Image non disponible

Ici, une petite difficulté supplémentaire : le résultat de la requête n'est plus modifiable à cause de la requête rStock qui intervient dans le modèle :

Image non disponible

La requête rfBouteilles ne peut pas être la source du formulaire fBouteilles sous peine qu'il devienne impossible de créer un nouvel enregistrement, impossible de modifier une donnée et impossible de supprimer un enregistrement :

Image non disponible

Pour contourner le problème, nous allons ruser : au lieu de prendre rfBouteilles comme source, nous allons prendre une requête qui ramène tous les enregistrements de tBouteilles qui sont sélectionnés par rfBouteilles, c'est-à-dire ceux qui sont ramenés par :

Image non disponible

Et comme source du formulaire, nous prendrons :

Image non disponible

V-A-4. Le code VBA associé au fonctionnement des filtres

V-A-4-a. Après mise à jour d'un filtre

Pour actualiser les enregistrements proposés :

 
Sélectionnez
Private Sub FiltrePays_AfterUpdate()
  Me.Requery
End Sub

V-A-4-b. Double-clic sur un contrôle de filtre

Pour annuler l'effet du filtre :

 
Sélectionnez
Private Sub FiltrePays_DblClick(Cancel As Integer)
  Me.ActiveControl = Null
  Me.Requery
End Sub

V-A-4-c. Clic sur le bouton « Tout afficher »

 
Sélectionnez
Private Sub BtTout_Click()
  Dim oCtl As Control
  'Réinitialiser les filtres standard
  For Each oCtl In Me.Controls
    If oCtl.Name Like "Filtre*" Then
        oCtl = Null
    End If
  Next oCtl
  'Réinitialiser caStock
  Me.caStock = 2
  'Réactualiser la sélection
  Me.Requery
End Sub

V-B. Dériver la région quand on connaît l'appellation

Image non disponible
Image non disponible

Remarquez la syntaxe pour désigner le contenu de la 3e colonne :

=[cboAppellationAcloner].column(2)

(2) est l'indice de la colonne. Access commence la numérotation des indices à zéro !

V-C. Afficher l'image de la bouteille de l'enregistrement en cours

Ou signaler son absence éventuelle.

Access permet de stocker des images directement dans la base de données. Que ce soit en tant que valeur du champ OLE d'une table ou en tant que propriété Image (Picture) d'un formulaire, d'un état ou d'un contrôle de l'un de ces deux objets. Toutefois, cela peut très vite alourdir la base de données si l'on a un grand nombre d'images en jeu.

cafeine, dans son tutoriel Gestion de photos par formulaire, a montré comment externaliser les images : il propose de stocker les adresses dans une table au lieu des images elles-mêmes.

Ici, nous allons un pas plus loin. Pas de table avec les adresses, mais une convention : les images seront toutes logées dans un sous-répertoire « Images » de l'application. Ainsi, quel que soit l'endroit où vous avez installé la base de données, le chemin complet de la photo sera CurrentProject.Path & "/Images/" & LeNomDeLaPhoto .

Et pour simplifier encore : on conviendra que le nom de la photo se compose de la clé de l'enregistrement avec l'extension « .jpg ».

Image non disponible

S'il s'avère que le fichier est absent, on affiche par défaut Default.jpg

Image non disponible

Voici le code VBA associé à l'événement « Sur Activation » du formulaire, il se déclenche à chaque lecture d'un enregistrement :

Private Sub Form_Current()
Sélectionnez
'Afficher l'image
  If existeFileFSO(CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg") Then
      Me.Image.Picture = CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg"
    Else
      Me.Image.Picture = CurrentProject.Path & "/Images/Default.jpg"
  End If

Remarquez que dans cette application, les images « statiques » (comme celle de ce bouton Image non disponible par exemple, sont aussi externalisées.

Si le sujet vous intéresse, voyez ce tutoriel Stockez les images statiques de vos formulaires et états Access hors de la base de données.

V-D. Rapatrier une image pour l'affecter à l'enregistrement en cours

Image non disponible

L'idée consiste à ouvrir une boîte de dialogue pour rechercher une image .jpg, quel que soit son nom actuel, pour en stocker une copie rebaptisée dans le sous-répertoire Images.

Voici le code associé au clic du bouton :

 
Sélectionnez
Private Sub BtAjouterImage_Click()
  Dim sOriginal As String
  On Error GoTo GestionErreurs
  Me.Refresh
  sOriginal = ChoisirUnFichier(Me.hwnd, "Quelle image .jpg voulez-vous affecter ?", 1, "Fichiers *.jpg", "jpg", "c:\")
  'Affecter l'image à l'enregistrement en cours
  FileCopy sOriginal, CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg"
  Me.Image.Picture = CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg"
GestionErreurs:
  Select Case Err.Number
    Case 0 'pas d'erreur
      Exit Sub
    Case 75 'aucun fichier n'a été choisi
      Exit Sub
    Case Else
      MsgBox "Erreur dans BtAjouterImage_Click  " & Err.Number & " " & Err.Description
  End Select
End Sub

N.B. Le code de la routine ChoisirUnFichier se trouve dans le module mArkham46.

Il s'agit d'une version améliorée par Arkham46 pour la rendre compatible 32 bits/64 bits.

V-E. Lancer une recherche Google en combinant des données affichées dans le formulaire

Image non disponible

Loufab a proposé une routine qui permet d'ouvrir un fichier avec le programme que l'utilisateur a associé à l'extension du fichier.

Il suffit de loger dans un module le code suivant :

Le module mLoufab
Sélectionnez
Option Compare Database
Option Explicit
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
    (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
    ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
            
Public Sub Ouvrir_fichier(Chemin As String)
  'Ouvrir, avec le pgm associé, le fichier dont le chemin est passé en paramètre
  ShellExecute Application.hWndAccessApp, "open", Chemin, "", "", 1
End Sub

On écrit Ouvrir_fichier(LeCheminDeCeFichier) et tout se passe comme si vous aviez double-cliqué sur le nom du fichier dans votre explorateur de fichiers.

Cette routine fonctionne aussi avec une URL, ainsi, Ouvrir_fichier ("http://www.google.com/") va entraîner l'affichage de la page d'accueil de Google par votre navigateur.

Et si vous voulez lancer directement une recherche, la syntaxe est
Ouvrir_fichier ("http://www.google.fr/search?q=les mots-clés séparés par un espacement")

Dans notre cas :

 
Sélectionnez
Private Sub btGoogle_Click()
  Ouvrir_fichier ("http://www.google.com/search?q=" _
              & Me.txtCruAcloner & " " & Me.txtMillesimeAcloner & " " _
              & Me.txtProprioNegoAcloner & " " & Me.cboCategorieAcloner.Column(1))
End Sub

V-F. Colorer un groupe d'options d'après le bouton radio

Image non disponible

La fonctionnalité « Mise en forme conditionnelle » n'est pas disponible pour un cadre d'options.

Qu'à cela ne tienne, nous allons ruser…

À côté du groupe d'options, nous plaçons une zone de texte avec certaines propriétés communes :

Image non disponible

Nous définissons les caractéristiques suivantes pour la mise en forme conditionnelle de la zone de texte :

Image non disponible

Remarquez que c'est la valeur du cadre d'options qui sert de référence à la mise en forme conditionnelle de la zone de texte.

Nous superposons ces deux contrôles, avec la zone de texte à l'arrière-plan.

Image non disponible

Pour garantir que la zone de texte ne reprenne l'avant-plan, ce code dans son événement « Sur réception du focus » :

 
Sélectionnez
Private Sub caAppreciationCouleur_GotFocus()
  Me.caAppreciation.SetFocus
End Sub

Un dernière retouche : à ce stade, le choix de l'option « À garder » donnerait ceci :

Image non disponible

Ce serait plus lisible si l'étiquette des boutons était en blanc. Allez, faisons joli !

Dans l'événement « Sur activation » du formulaire et après mise à jour de caAppreciation, nous déclencherons cette routine :

 
Sélectionnez
Public Sub CouleurEtica()
  Dim i As Integer
  If Me.caAppreciation = 2 Then 'en blanc
      For i = 1 To 3
        Me("etica" & i).BorderColor = 16777215 'blanc
        Me("etica" & i).ForeColor = 16777215 'blanc
      Next i
    Else
      For i = 1 To 3
        Me("etica" & i).BorderColor = 0 'noir
        Me("etica" & i).ForeColor = 0 'noir
      Next i
  End If
End Sub

Et voilà le travail !

Image non disponible

V-G. Supprimer une bouteille de la base

Image non disponible

Pour supprimer un enregistrement de la table tBouteilles, il faut d'abord supprimer les enregistrements des autres tables qui se réfèrent (éventuellement) à la clé primaire de l'enregistrement à supprimer.

Dans notre cas, il s'agit des enregistrements (éventuels) de tMouvements :

Image non disponible

On demandera également le sort à réserver à l'image (éventuellement) associée à l'enregistrement.

Pour éviter les regrets éternels, on demande d'abord à l'utilisateur de confirmer ses intentions destructrices.

 
Sélectionnez
Private Sub BtSupprimer_Click()
  Dim iReponse As Integer
  iReponse = MsgBox("Confirmez que vous voulez supprimer cet article", vbOKCancel + vbDefaultButton2)
  If iReponse = vbCancel Then Exit Sub
  'Supprimer l'image (éventuellement)
  If existeFileFSO(CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg") = True Then
      iReponse = MsgBox("Voulez-vous aussi supprimer l'image ?", vbYesNo + vbDefaultButton1)
         If iReponse = vbYes Then
            Kill (CurrentProject.Path & "/Images/" & Me.txttBouteillesPK & ".jpg")
         End If
  End If
  DoCmd.SetWarnings False
  'Supprimer les mouvements (éventuels)
  DoCmd.RunSQL ("DELETE * FROM tMouvements WHERE tBouteillesFK=" & Me.txttBouteillesPK & ";")
  DoCmd.RunSQL ("DELETE * FROM tBouteilles WHERE tBouteillesPK=" & Me.txttBouteillesPK & ";")
  DoCmd.SetWarnings True
  Me.Requery
End Sub

V-H. Comment créer un article quasi semblable à l'enregistrement actif

Image non disponible

Une belle occasion d'utiliser la propriété Remarque (Tag) d'un contrôle !

Voici une propriété rarement utilisée et pourtant bien pratique ! C'est un pense-bête : vous pouvez y inscrire ce que vous voulez avec un maximum de 2048 caractères.

Contrairement aux autres propriétés, le paramétrage de celle-ci n'affecte pas les attributs d'un objet.

La première idée pour créer un enregistrement quasi semblable, c'est de d'abord le copier et ensuite modifier ce qu'il faut.
Dans notre cas, ce n'est pas possible, car pour éviter les doublons (deux bouteilles identiques dans notre base), nous avons placé un index unique sur les colonnes de la table qui singularisent un article :

Image non disponible

Donc, la tentative d'ajouter d'abord un enregistrement identique à celui affiché échouerait.

Il faut trouver un moyen pour pouvoir modifier au moins une valeur avant d'ajouter le nouvel enregistrement dans tBouteilles.

Au fait, quels champs faut-il cloner ? Eh bien ! ceux- ci :

Image non disponible

Pour simplifier l'écriture du code, nous avons donné un nom « particulier » aux contrôles qui contiennent ces champs, leur nom se termine par « Acloner » : txtCruAcloner, txtMillesimeAcloner, cboAppellationAcloner…

Quand l'utilisateur clique le bouton, le programme va :
- copier le contenu de chaque contrôle suffixé « Acloner » dans sa propriété Remarque ;
- proposer un enregistrement vierge ;
- dans chaque contrôle suffixé « Acloner », reporter le contenu de la propriété Remarque dans la propriété Valeur.

À ce moment, l'utilisateur reprend la main pour modifier ce qu'il faut et sauvegarder ce nouvel enregistrement.

Voici le code :

 
Sélectionnez
Private Sub BtCloner_Click()
  Dim oCtl As Control
  'On Error GoTo GestionErreurs
  Me.Refresh
  'une boucle sur tous les contrôles, détecter ceux *Acloner et copier leur contenu
  For Each oCtl In Me.Controls
    If oCtl.Name Like "*Acloner" Then
        Me(oCtl.Name).Tag = Nz(Me(oCtl.Name), " ") 'pour les cas  un champ texte est vide
    End If
  Next oCtl
  'Ouvrir un nouvel enregistrement
  DoCmd.GoToRecord , , acNewRec
  'Retransférer Remarque dans Value
  For Each oCtl In Me.Controls
    If oCtl.Name Like "*Acloner" Then
        Me(oCtl.Name) = Me(oCtl.Name).Tag
    End If
  Next oCtl
GestionErreurs:
  Select Case Err.Number
    Case 0 ' pas d'erreur
    Case Else
      MsgBox "Erreur  " & Err.Number & " " & Err.Description & vbLf _
                & "dans BtCloner_Click."
  End Select
End Sub

V-I. Comptabiliser une entrée ou une sortie

Image non disponible

Le clic sur l'un des boutons ouvre le formulaire fMouvements en adaptant sa présentation :

Image non disponible

Remarquez que le formulaire fMouvements s'affiche juste en dessous du bouton cliqué, quelle que soit la position du formulaire fBouteilles à l'écran !

Clic sur entrée
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Private Sub BtEntree_Click()
  Me.Refresh 'utile si l'enregistrement est en cours de création
  'S'ils sont ouverts, refermer fJLEntreesSorties et fMouvements
  If CurrentProject.AllForms("fJLEntreesSorties").IsLoaded Then DoCmd.Close acForm, "fJLEntreesSorties"
  If CurrentProject.AllForms("fMouvements").IsLoaded Then DoCmd.Close acForm, "fMouvements"
  'Ouvrir fMouvements juste en dessous du bouton (méthode Arkham46)
  DoCmd.OpenForm "fMouvements", , , , , acHidden
  PositionForm Forms("fMouvements"), Me.BtEntree
  'Adapter la forme de fMouvements pour une entrée
  Forms!fMouvements!txtTitre = "Entrée" & Chr(13) & Chr(10) _
                              & "saisissez un nombre positif" & Chr(13) & Chr(10) _
                              & "dans le champ Nombre."
  Forms!fMouvements!TXTtBouteillesFK = Me.txttBouteillesPK
  Forms!fMouvements.Section("PiedFormulaire").Visible = False
 End Sub

Commentaires du code

N° d'instruction  
3 à 8

Image non disponible

Ouverture du formulaire fMouvements par rapport à la position du bouton cliqué.
Cette routine est proposée à cette adresse par Arkham46.
Même si vous ne comprenez pas à quoi servent toutes les API auxquelles il fait appel dans son code, faites confiance : ça marche au poil !
Il suffit de copier le code proposé dans un module (dans notre base exemple, c'est le module mArkham46).
Et pour l'utiliser, deux lignes de code :

 
Sélectionnez
DoCmd.OpenForm "FormulaireAPositionner", , , , , acHidden
PositionForm Forms("FormulaireAPositionner"), Me.UneZoneDeTexte

Deux précautions :

  • le formulaire à ouvrir doit être en fenêtre indépendante ;
  • en cas d'appels successifs, refermez d'abord le formulaire précédemment ouvert avant de l'ouvrir une nouvelle fois.
10 à 12 On adapte la forme de fMouvements (titre et contrôles visibles selon le bouton cliqué).
13 On copie le tBouteillesPK de l'enregistrement actif de fBouteilles dans le tBouteillesFK de fMouvements.
14 On cache les contrôles spécifiques à l'autre bouton (ici on cache ceux qui concernent une sortie).

Et un code semblable pour un clic sur l'autre bouton :

 
Sélectionnez
Private Sub BtSortie_Click()
  'S'ils sont ouverts, refermer fJLEntreesSorties et fMouvements
  If CurrentProject.AllForms("fJLEntreesSorties").IsLoaded Then DoCmd.Close acForm, "fJLEntreesSorties"
  If CurrentProject.AllForms("fMouvements").IsLoaded Then DoCmd.Close acForm, "fMouvements"
  'Ouvrir fMouvements juste en dessous du bouton (méthode Arkham46)
  DoCmd.OpenForm "fMouvements", , , , , acHidden
  PositionForm Forms("fMouvements"), Me.BtSortie
  'Adapter la forme de fMouvements pour une sortie
  Forms!fMouvements!txtTitre = "Sortie" & Chr(13) & Chr(10) _
                              & "saisissez un nombre négatif" & Chr(13) & Chr(10) _
                              & "dans le champ Nombre."
  Forms!fMouvements!TXTtBouteillesFK = Me.txttBouteillesPK
  Forms!fMouvements!etiPrix.Visible = False
  Forms!fMouvements!txtPrix.Visible = False
End Sub

Dans les zones de liste, si l'utilisateur propose un texte qui n'est pas encore dans les choix prévus, le programme propose de l'ajouter :

Image non disponible
 
Sélectionnez
Private Sub cboBuAvec_NotInList(NewData As String, Response As Integer)
  If MsgBox("Voulez-vous ajouter " & NewData & " à la liste des convives ?", _
                       vbYesNo + vbQuestion + vbDefaultButton2, "Ajout") = vbYes Then
        DoCmd.SetWarnings False
        DoCmd.RunSQL "INSERT INTO tBuAvec ( BuAvec ) SELECT """ & NewData & """;"
        DoCmd.SetWarnings True
        Response = acDataErrAdded
    Else
        Response = acDataErrContinue
        Me.cboBuAvec.Undo
  End If
            
End Sub
            
Private Sub cboBuSur_NotInList(NewData As String, Response As Integer)
  If MsgBox("Voulez-vous ajouter " & NewData & " à la liste des mets dégustés ?", _
                       vbYesNo + vbQuestion + vbDefaultButton2, "Ajout") = vbYes Then
        DoCmd.SetWarnings False
        DoCmd.RunSQL "INSERT INTO tBuSur ( BuSur ) SELECT """ & NewData & """;"
        DoCmd.SetWarnings True
        Response = acDataErrAdded
    Else
        Response = acDataErrContinue
        Me.cboBuAvec.Undo
  End If
End Sub
            
Private Sub cboCommentaire_NotInList(NewData As String, Response As Integer)
  If MsgBox("Voulez-vous ajouter " & NewData & " à la liste des commentaires ?", _
                       vbYesNo + vbQuestion + vbDefaultButton2, "Ajout") = vbYes Then
        DoCmd.SetWarnings False
        DoCmd.RunSQL "INSERT INTO tCommentaires ( Commentaire ) SELECT """ & NewData & """;"
        DoCmd.SetWarnings True
        Response = acDataErrAdded
    Else
        Response = acDataErrContinue
        Me.cboBuAvec.Undo
  End If
End Sub

C'est l'adaptation du code proposé dans la FAQ : https://access.developpez.com/faq/?page=zdl#AbsDsListe.

V-J. Voir l'historique des mouvements pour une bouteille

Image non disponible

Le clic sur ce bouton ouvre le formulaire fJLEntreesSorties :

Image non disponible

Il est seulement possible de modifier un enregistrement, pas d'ajout ni de suppression :

Image non disponible

En fait, pour « neutraliser » un enregistrement, il suffit d'encoder 0 dans le nombre, l'enregistrement sera alors physiquement supprimé lors de la fermeture de fBouteilles (voir l'événement Private Sub Form_Close() de fBouteilles).

Lorsque l'utilisateur s'apprête à modifier un nombre, le programme contrôle d'abord que cette modification n'entraînera pas un stock négatif :

 
Sélectionnez
Private Sub txtMvt_BeforeUpdate(Cancel As Integer)
  'Vérifier que la modif ne provoquera pas un stock négatif
  If Forms!fBouteilles!txtStock - Me.txtMvt.OldValue + Me.txtMvt.Value < 0 Then
    MsgBox "Cette modification entraînerait un stock négatif !", vbCritical
    Cancel = True
    Me.txtMvt.Undo
  End If
End Sub

Si c'est le cas, la modif est refusée et un message est affiché :

Image non disponible

VI. Quelques commentaires sur les états

Image non disponible

VI-A. Liste des vins bus avec_

Image non disponible

La source

Image non disponible

VI-B. Liste des entrées depuis le_ jusqu'au_

Image non disponible

La source

Image non disponible

Remarquez le critère pour le MvtDate :

Image non disponible

- si l'utilisateur n'a pas complété la date de départ (elle est donc Null), elle sera remplacée par une date lointaine dans le passé (01/01/1900) ;

- pareil pour l'absence de la date de fin (date du jour).

En d'autres mots, si les dates sont null, on liste depuis le début jusqu'à aujourd'hui.

Image non disponible

VI-C. Répartition des bouteilles par région

Image non disponible

La source

On part de la requête rRepartition :

Image non disponible

… que nous allons convertir en requête d'analyse croisée à l'aide de l'interface proposée par Access.

Voici les différentes étapes :

1° affichez la fenêtre des objets à l'onglet « Requêtes ». Cliquez sur « Nouveau ». Dans la fenêtre qui vient, sélectionnez « Assistant Requête analyse croisée » et cliquez le bouton « Suivant> » :

Image non disponible

2° répondez à la série de questions :

Image non disponible
Image non disponible

3° terminez en donnant un nom significatif à la requête croisée :

Image non disponible

4° il vient :

Image non disponible

… qui servira de source à l'état eRepartition :

Image non disponible

VI-D. Inventaire par Pays/Région

Image non disponible

La source

Image non disponible

Procédure pour imprimer sur deux colonnes

Image non disponible

VII. Conclusion

Ce tutoriel a été l'occasion de montrer quelques réalisations pratiques qui répondent à des questions souvent posées sur le forum :

- mettre en place une gestion de stock, avec une évaluation au dernier prix d'achat ;

- construire un formulaire de recherche multicritère basé sur une requête enregistrée qui est non modifiable ;

- afficher une image différente pour chaque enregistrement et logeant ces images en dehors de la base ;

- montrer une astuce pour contourner l'absence de mise en forme conditionnelle pour un cadre d'options ;

- décrire une méthode pour cloner un enregistrement et ses dépendants éventuels dans les tables liées à la sienne ;

- construire un état basé sur une requête d'analyse croisée ;

- imprimer un état sur plusieurs colonnes, quand cela permet d'économiser du papier.

Image non disponible

VIII. Téléchargement

La base de données au format Access2000 et un sous-répertoire Images réduit :

http://claudeleloup.developpez.com/tutoriels/access/cave-a-vin/CaveAvin.zip (10 608 ko).

Pour plus d'images de bouteilles, remplacez le sous-répertoire Images réduit par celui-ci :

http://claudeleloup.developpez.com/tutoriels/access/cave-a-vin/ToutesLesImages.zip (120 142 ko).

IX. Remerciements

Un grand merci à :

marcdels pour sa contribution à la mise au point de cette application et notamment les exemples et les tests des fonctionnalités ;

tee_grandbois pour ses explications claires sur le forum ;

Jacques THERY pour la relecture orthographique.

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

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Claude Leloup. 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.