Opérations Mathématiques Inter-Enregistrements

Retour vers Access

Opérations Mathématiques Inter-Enregistrements

Constat : De nombreuses personnes, moi le premier, souhaitent obtenir des totaux ou sous totaux dans un champ, un peu à la manière d'Excel où l'on peut additionner ou soustraire le contenu de la "ligne" précédente. Or Access, en règle générale, se cantonne à travailler dans l'enregistrement en cours... nous devons donc lui donner l'ordre de parcourir les enregistrements pour récupérer ou utiliser une donnée qui n'appartient pas à l'enregistrement en cours
Exemples : nous allons en décortiquer deux ...
1er But : Nous souhaitons créer un simple report d'un enregistrement à l'autre...

  1. KmFin de l'Enr N°1 vers KmDebut de l'Enr N°2 et ainsi de suite...
  2. Conditions KmFin sera toujours plus grand que KmDebut et il ne pourra pas y avoir de KmDebut = 0

Nous créons une table toute simple, on remarquera que l'on utilise un champ NumeroAuto; celui-ci nous permet de conserver l'ordre des enregistrements. En effet, si l'on venait à créer des filtres ou des tris, la cohérence de nos résultats pourraient sembler inexacts à l'écran. Il est donc important de pouvoir présenter les enregistrements dans un ordre précis...

Présenter les enregistrements dans l'ordre de la saisie est le seul usage de cette numérotation automatique, ainsi cette numérotation pourra être discontinue si nous effaçons des enregistrements, etc...comme on peut le voir sur la requête créée sur la table (cette vue n'a pas été prise au même moment que pour l'image du formulaire, ne vous inquiétez donc pas des enregistrements affichés - je vous démontre qu'il n'y a aucun entretien de la numérotation automatique).

On définit un tri ascendant

Nous allons travailler sur la propriété "Après MAJ" du champ [KmFin]

Il y a dans la procédure deux grands chapitres, soit une mise à jour d'un enregistrement déjà encodé, soit l'ajout d'un nouvel enregistrement. Si une de nos conditions n'est pas remplie la "colonne" se colore en rouge pour attirer l'attention de l'utilisateur

 

Private Sub KmFin_AfterUpdate()
  Dim vFin As Double, rs As DAO.Recordset, decompte As Integer
  Dim KmCtl As Double
  'initialisation des données
  vFin = 0: KmCtl = 0: decompte = 0
  'ligne indispensable pour que la nouvelle valeur de KmFin soit prise
  'en compte, sinon on est en mode édition et la nouvelle valeur est
  'à null ce qui provoque une erreur
  Me.Refresh

  'initiation de la copie du recordset
  'on se retrouve automatiquement sur le dernier Enr
 Set rs = Me.RecordsetClone

  'on se positionne sur le premier enregistrement
  rs.MoveFirst
  'on va boucler
  Do
  'on récupère la valeur du champ
  vFin = rs("KmFin")
  KmCtl = rs("KmDebut")
  '***************************
  'Procédure d'ajout Enr
  '***************************
  'Pour détecter fin de fichier moins un Enr
  decompte = rs.RecordCount-1
  If decompte = (rs.RecordCount - 1) Then

  If vFin < KmCtl And vFin <> 0 Then
    MsgBox "KmFin doit être plus grand que KmDebut"
    vFin = 0
    [KmFin].BackColor = RGB(255, 0, 0)
    Exit Sub
 Else
    [KmFin].BackColor = RGB(255, 255, 255)
  End If

  'on passe sur l'enregistrement suivant
  rs.MoveNext
  'on ajoute
  rs.AddNew
  'on file la valeur au champ voulu
  rs("KmDebut") = vFin
  'on donne l'ordre de se mettre à jour
  rs.Update
  GoTo Test

  End If
  '********************
  Pour mettre à jour le champ KmDebut
  '********************

  If vFin < KmCtl Then
     MsgBox "KmFin doit être plus grand que KmDebut"
     vFin = 0
    [KmFin].BackColor = RGB(255, 0, 0)
    Exit Sub
  Else
    [KmFin].BackColor = RGB(255, 255, 255)
  End If

  'mise à jour de l'enregistrement suivant
  rs.MoveNext

  rs.Edit
  rs("KmDebut") = vFin
  rs.Update

  decompte = decompte + 1
  'on boucle jusqu'à la fin de fichier
  Loop Until rs.EOF = True

  Test:
  'parce qu'une telle ligne ne nous sert à rien
  'en fin de fichier
  rs.MoveLast
  If rs("KmDebut") = 0 And rs("KmFin") = 0 Then
    rs.Delete
 End If

  'on récupère de la mémoire
  Set rs = Nothing
End Sub

2ème But : Cette fois, nous allons gérer un fichier de stock, des entrées, des sorties et au final, nous devons toujours être capable de dire combien d'articles nous possédons. Un petit dessin vaut mieux qu'un long discours...Même remarques pour le champ [Nu] que dans l'exercice précédent.

Dans l'image précédente, on constate que c'est peu lisible, les pommes et les poires sont mélangées...mais que dites-vous de ceci

L'on utilise des boutons pour afficher l'item voulu. Ceux-ci lancent le code suivant :

Private Sub Commande9_Click()
  DoCmd.ApplyFilter , "Article = 'Pommes'"
End Sub

On peut faire mieux en utilisant un double clic dans le champ "Article" et l'utilisateur peut alors trier des articles pour lesquels aucun bouton n'a été prévu.

Private Sub Article_DblClick(Cancel As Integer)
  DoCmd.ApplyFilter , "Article= '" & [Article] & "'"
End Sub

Le code final qui tout en recalculant tous les enregistrements parvient à provoquer une rupture lorsqu'il rencontre une valeur différente dans le champ "Article"

Sub calcul()
  'Comme on peut toujours se tromper et mettre un champ à blanc
  On Error Resume Next
  'on réalise une copie des enregistrements en mémoire
  Dim rs As DAO.Recordset
  Set rs = Me.RecordsetClone

  'Obligatoire, pour enregistrer la nouvelle valeur sinon
  'Access considère la valeur du champ comme Null = Bug !
  'ou il reprend l'ancienne valeur en compte
  Me.Refresh

  'Déterminons des variables correspondant à nos champs
  'Ceci nous permet de glisser des valeurs issues de ceux-ci
  'à un moment x
  Dim vIn, vOut, vStock, vAncienStock As Double
  Dim vArticle As String

 'inutile de parcourir le code si la table est vide
  If rs.RecordCount > 0 Then

'Au cas où un utilisateur oublie de mettre une valeur dans le champ
  If vArticle = "" Then
    vArticle = " "
 Else
    vArticle = rs("Article")
  End If

  vStock = 0
  vAncienStock = 0
  'il faut récupérer la valeur de l'enr en cours
  'la comparaison avec les valeurs des autres enregistrements
  'nous permettra de provoquer la rupture de l'opération mathématique (Total)
  vArticle = [Article]

  'se placer sur le premier enregistrement pour éviter qu'Access
  'ne vienne buter dans le néant et émette des messages de protestation
  rs.MoveFirst

  Do While rs.EOF = False
  'C'est cette comparaison qui permet de regrouper virtuellement
  ' les enregistrements par "Article"
  If vArticle = rs("Article") Then
  vIn = rs("In")
  vOut = rs("Out")
  'les opérations seront aussi plus simples
  vStock = vIn - vOut
  'Maintenant il faut mettre à jour les champs
  'on doit passer en mode édition
  rs.Edit
  rs("Stock") = vAncienStock + vStock
  'on passe en mode mise à jour
  rs.Update
  'Pour pouvoir se resservir de cette valeur dans l'Enr Suivant
  vAncienStock = rs("Stock")
  End If  ' de vArticle
  rs.MoveNext
  Loop

  End If ' de recordcount
  Set rs = Nothing
End Sub

Pour les néophytes, mais qu'est ce que tout ce charabia signifie ? En fait, le mécanisme est très simple. On se positionne sur le premier enregistrement. Et l'on commence à boucler sur les enregistrements (Do....Loop) jusqu'à la fin de la table (While rs.EOF=False). Avant le départ de la boucle, on récupère la valeur du champ ("Article") dans vArticle. A chaque enregistrement (c'est le rôle de la boucle de nous y conduire), l'on compare le champ "Article" avec la variable vArticle, si le test est positif : on poursuit le code et on incrémente les valeurs nécessaires (on réalise AncienStock + In - Out = Stock). Avant de quitter l'enregistrement que l'on a atteint, on place la valeur du champ "Stock" dans la variable AncienStock , pour pouvoir intégrer cette valeur dans le calcul suivant...et ainsi de suite ...

Et enfin, il faut aussi prévoir le déclenchement de la procédure au chargement ou l'activation du formulaire

Cliquez ici pour télécharger le fichier ReportKm

Fin