Dès que nous voulons développer une application de base de données un peu évoluée, nous sommes très vite confrontés à un problème épineux : comment récupérer la valeur des enregistrements précédents pour nous permettre de les manipuler à loisir dans des fonctions de comparaison, ou d'opérations mathématiques (addition, soustraction, multiplication, division) ou autres fantaisies.
Nous allons tenter d'apporter une réponse. Je dis bien tenter car de ma faible expérience, j'ai remarqué certaines anomalies dans la politique VBA chez microsoft : en effet, vous devez savoir que la plupart des procédures, méthodes, fonctions principales sont contenues dans les fameux .DLL dont nos ordinateurs regorgent. Ces fichiers sont comme des boîtes qui contiennent des mécanismes dont on a du mal à découvrir les fonctions exactes tant la documentation est rare. On ne sait ce qu'ils contiennent exactement, nous n'avons aucune emprise sur leur code...Bref, ceci pour en venir à quoi ? Simplement que chez MS, ils font évoluer ces .DLL et qu'il réécrivent certaines d'entres elles en modifiant la syntaxe et parfois même les paramètres que l'on doit leur transmettre...Si bien que j'ai remarqué que d'une configuration à l'autre, sur des MS Access de même génération, on n'obtient pas le même résultat et ce jusqu'à faire planter l'application.
On peut déterminer quelle .DLL on veut employer, car certaines versions peuvent très bien cohabiter, mais il faut savoir que certaines d'entre elles s'excluent. Bref, un casse tête pour les néophytes, y compris votre serviteur...Où peut-on tenter de régler cela ? et bien par tâtonnement ou par une documentation trouvée au gré des lectures...et dans Access, lorsque vous êtes dans VBE, allez sur le menu Outils - Références et cochez les cases adéquates comme ceci :
Celles qui vont nous intéresser surtout sont les ActiveX Data Objects et les DAO Objet Library, car c'est avec celles-là que nous allons dialoguer...rassurez-vous de manière transparente... mais il est bon de connaître leur existence car si vous n'obtenez pas le même résultat que moi dans l''exercice qui va nous occuper , c'est peut être dans cette direction qu'il faut chercher
Anecdotes personnelles :
- Travaillant sous NT4 dans une base de données partagée sur le réseau d'entreprise, j'implémente une fonction faisant appel à une API pour récupérer le nom de l'utilisateur connecté (UserName NT), j'obtiens le code sur différents sites...sur ma machine, ce code fonctionne. Tout heureux, je remets l'application en service. Hélas, je devais déchanter, des utilisateurs généraient des erreurs à l'exécution. Après étude, je remarque que ces machines étaient déjà sous Service Pack 6 pour NT, or mon ordi était encore sous Service Pack 4, j'upgrade ma machine...bardaf ! le code plante chez moi maintenant... Aie, aie !, bon !, recherche et travail m'ont finalement permis de trouver une syntaxe qui fonctionne...
- Idem pour le code ci-dessous, sous Access XP, la première solution a fonctionné de suite, par contre il a fallu que je découvre qu'il fallait déclarer dans quel mode (ADO ou DAO) j'abordais les recordsets pour que la seconde solution soit acceptée. Pire, avant cela, je déactivais les ActiveX Data Objet, je lance l'exécution de la méthode DAO, sans la déclaration spécifique, ça marche...par contre la méthode ADO plante, bien sûr... je réactive les ADO, je teste..et bien la méthode DAO fonctionne maintenant (même sans déclaration spécifique) alors qu'elle avait refusé jusqu'à présent, grrrr...!!! Bon, ne prenons plus de risque et travaillons en déclarant complètement nos objets
Et maintenant, l'exercice ...
Scénario : Un débit de boisson,on sert les client, et on manipule une caisse enregistreuse pour calculer l'addition. Voici ce que je veux obtenir...
Lorsque j'appuie sur un des boutons, la consommation s’inscrit dans le sous-formulaire comme un nouvel enregistrement, je fournis simplement la quantité de verres - paramètre que l'on ne peut pas prévoir. Le prix par type et l'addition totale se calculent. Et SEULEMENT AINSI, n'inscrivez pas vous-même le nom de la boisson dans le F_DetailCommande
Bien sûr, à partir de là, on peut imaginer la gestion complète d'un établissement, les stocks, les employés, la comptabilité , etc...etc...mais nous nous limiterons simplement à notre scénario même si dans les explications, vous pourrez entrevoir la puissante de l'application. Un peu comme la base de donnée fournie en exemple : Les Comptoirs. Mon but ici est d'isoler la manière de parcourir les enregistrements, pour pouvoir la calquer ensuite très facilement à d'autres applications, car c'est un problème récurrent
Nous commençons par créer quelques tables toutes simples
Sur lesquelles, nous bâtissons des requêtes
un formulaire et un sous formulaire
Enfin, un module dont nous verrons l'utilité et le code tout à l'heure
Nous aurions pu ajouter un Etat qui aurait pu simuler le ticket de caisse, mais cela n'apporte pas grand chose de neuf
L'activité principale se concentre sur les événements "clic" des différents boutons, voici le code pour le 3ème bouton "Pelforth Brune". Cette procédure sera reproduite et adaptée pour chaque bouton (on pourrait créer un module vers lequel on enverrait des paramètres comme le nom du produit etc... pour se rapprocher d'une programmation de puriste et ne pas faire de copier-coller, mais je pense que l'on peut envisager d'abord les choses simplement...Apprenons à marcher, nous courrons plus tard
Première méthode: ADO
Private Sub Commande27_Click()
'On Error GoTo Err_Commande27_Click
'1ère sous routine on récupère le prix de vente de T_GestionProduit
Dim rs1 As New ADODB.Recordset
Dim rsPrixVente As Currency
Dim rsQuantite As Integer
rs1.Open "T_GestionProduit", CurrentProject.Connection, adOpenDynamic
rs1.Find "Produit = 'Pelforth Brune'"
rsPrixVente = rs1("PrixVente")
rs1.Close
Set rs1 = Nothing
rsQuantite = InputBox("Quelle quantité ?", "Combien...")
'2ème sous routine on remplit le détail de la commande
Dim rs2 As New ADODB.Recordset
Dim rsNom As String
Dim rsPrix As Currency
rs2.Open "T_DetailsCommande", CurrentProject.Connection, adOpenDynamic, adLockOptimistic
rs2.AddNew
rs2("RefCommande") = Me.RefCommande
rs2("Produit") = "Pelforth Brune"
rs2("Prix") = rsPrixVente
rs2("Quantite") = rsQuantite
rs2("Total") = rs2("Prix") * rsQuantite
rs2.Update
rs2.Close
Set rs2 = Nothing
Call CalculAddition
Me.Refresh
Exit_Commande27_Click:
Exit Sub
Err_Commande27_Click:
MsgBox Err.Description
Resume Exit_Commande27_Click
End Sub
On remarque qu'en fin de code, il y a un appel vers une autre procédure. Celle-ci est contenue dans un module. car on peut y faire appel sur d'autres événements, pour permettre un rafraîchissement du montant de l'addition, au cas où l'on supprime des enregistrements, ou lorsque l'on change d'enregistrement, etc..
Option Compare Database
Option Explicit
Sub CalculAddition()
'3ère sous routine on récupère le prix de vente de T_GestionProduit
Dim rs3 As New ADODB.Recordset
Dim rs3Addition As Currency
rs3.Open "T_DetailsCommande", CurrentProject.Connection, adOpenDynamic
rs3Addition = 0
rs3.MoveFirst
Do
If rs3("RefCommande") = Form_F_Commandes.RefCommande Then
rs3Addition = rs3("Total") + rs3Addition
End If
rs3.MoveNext
Loop Until rs3.EOF
Form_F_Commandes.Addition = rs3Addition
rs3.Close
Set rs3 = Nothing
Form_F_Commandes.Refresh
End Sub
Voilà ! c'est une des méthodes qui permet de parcourir les enregistrements pour retrouver certaines valeurs (Prix de vente) et qui permet de réaliser un calcul sur une "colonne" (Addition)...
Seconde méthode: DAO
Seconde méthode qui fonctionne sur Access 97. Le code sera tout à fait différent et un peu plus universel dans les environnements Access (97 - 2000 - 2002)
Private Sub Commande28_Click()
On Error GoTo Err_Commande28_Click
'1er procédure on retrouve le prix de vente
Dim db As DAO.Database, rs1 As DAO.Recordset, rs2 As DAO.Recordset
Dim strSQL1 As String, strSQL2 As String
Dim Prix As Currency, Addition As Currency, Com As Integer
Set db = CurrentDb
strSQL1 = "SELECT * FROM T_GestionProduit"
Set rs1 = db.OpenRecordset(strSQL1)
Set rs2 = db.OpenRecordset("T_DetailsCommande", dbOpenTable)
Com = Me.RefCommande
rs1.MoveFirst
Do
If rs1("Produit") = "Get 27" Then
Prix = rs1("PrixVente")
End If
rs1.MoveNext
Loop Until rs1.EOF = True
With rs2
.AddNew
!RefCommande = Com
!Produit = "Get 27"
!Prix = Prix
!Quantite = InputBox("Quelle quantité ?", "Combien...")
!Total = Prix * !Quantite
.Update
End With
rs1.Close
rs2.Close
Set db = Nothing
Me.Refresh
Call CalculAddition
Exit_Commande28_Click:
Exit Sub
Err_Commande28_Click:
MsgBox Err.Description: Resume Exit_Commande28_Click
End Sub
En fushia, la déclaration qui m'a tant fait défaut dans les premières heures...En vert, les lignes sur lesquelles le code butait et deux manières d'instancier les recordsets
Cliquez ici pour télécharger le fichier zip RestoAccess2000