J'ai une base de données MS Access avec plusieurs tables, et chaque table a des champs différents (chaque table a une clé primaire). Chaque fois que quelqu'un modifie un enregistrement existant, je veux que l'état avant/après soit enregistré dans une table "historique", mais j'essaie d'éviter de créer une DataMacro pour plus de 20 champs par table, car cela semble lourd et difficile à gérer.
Pour ce faire, j'ai ajouté des macros "Before Change" et "After Update" qui ont chacune un appel à la fonction SetLocalVar pour exécuter des fonctions vba publiques qui résident dans un module local. La fonction "before" parcourt les champs existants dans la table actuelle pour construire un dictionnaire des valeurs avant modification. La fonction "after" répète le processus pour identifier le champ qui a changé, de sorte que l'information peut être ajoutée à un tableau d'historique (contient le nom de la table, le nom du champ, les valeurs avant/après, l'utilisateur, l'horodatage, etc.)
Le problème que je rencontre est que la fonction "Après la mise à jour" ne révèle que les données antérieures à la modification. Je ne peux pas dire quelle est la nouvelle valeur ou quel champ a été modifié. Je ne peux pas utiliser [Old].[FieldName],[FieldName] comme entrées pour SetLocalVar car je ne sais pas à l'avance quels champs seront mis à jour (et les noms des champs diffèrent selon les tables). Et je ne peux pas appeler Requery ou Refresh car cela entre en collision avec le processus "After Update".
De toutes les questions similaires posées ici et ailleurs que je peux trouver, elles commencent soit par savoir explicitement quel champ va changer, soit par ne pas fournir ce niveau de détail dans leurs événements de journal. Ou peut-être que mon google-fu est faible.
Voici ce que j'ai :
Antes de SetLocalVar (nom de table d'entrée différent par table) :
=SetupLogEvent("Table01",[KeyName])
Après SetLocalVar expression :
=SubmitLogEvent("Table01",[KeyName])
Code du module
Public BeforeFields As Scripting.Dictionary
Public Function SetupLogEvent(ByVal TableName As String, ByVal KeyName As String)
Set BeforeFields = New Scripting.Dictionary
Set rs = CurrentDb.OpenRecordset("SELECT * FROM [" & TableName & "] WHERE KeyName='" & KeyName & "'")
Dim i As Long
With rs
.MoveLast
.MoveFirst
For i = 0 To rs.Fields.Count - 1
fName = rs.Fields(i).Name
fVal = rs.Fields(i).Value
BeforeFields.Add fName, fVal
Next i
End With
rs.Close
SetupLogEvent = True
End Function
Public Function SubmitLogEvent(ByVal TableName As String, ByVal KeyName As String)
DoEvents
Dim MakeUpdate As Boolean
MakeUpdate = False
Set rs = CurrentDb.OpenRecordset("SELECT * FROM [" & TableName & "] WHERE KeyName='" & KeyName & "'")
With rs
.MoveLast
.MoveFirst
For i = 0 To rs.Fields.Count - 1
fName = rs.Fields(i).Name
fVal = rs.Fields(i).Value
If fVal <> BeforeFields(fName) Then
Debug.Print ("Modified field is " & fName)
MakeUpdate = True
Exit For
End If
Next i
End With
rs.Close
If MakeUpdate = True Then
'Run SQL code to update the history table with the relevant information
'DoCmd.RunSQL <SQL code here>
End If
Set BeforeFields = Nothing
SubmitLogEvent = True
End Function
Je pense que je devrais utiliser une autre méthode que celle qui consiste à parcourir le jeu d'enregistrements, mais je ne suis pas sûr de ce que je devrais faire à la place. Je suppose que je pourrais ajouter deux douzaines d'entrées facultatives à la fonction after et introduire manuellement chaque [nom de champ] de chaque table dans l'expression SetLocalVar de chaque table, mais je n'arrive pas à imaginer que le système soit inflexible au point d'exiger cela. Il doit y avoir un meilleur moyen, non ?
Edit : J'ai mis à jour le titre de cette question et la raison pour laquelle j'ai évité d'écrire une DataMacro pour chaque champ pour les futurs lecteurs de ce sujet.