Bonjour.

Je ne savais pas trop comment nommer cette discution :

- utilisation d'une liste des champs d'une table, via recordset, imbriquée dans un autre recordset, problème de champ vide et/ou format -

J'ai assemblé plusieurs parties de code dans le but de prendre un fichier texte pré-formaté et de le personnaliser avec des données se trouvant dans une table et ce, pour chaque enregistrement de la table.

En fonction d'un traitement préalable, pour chaque enregistrement, le fichier texte source est le même, ou différent et indiqué par un champ spécifique de la table.
Il est copié, modifié, renommé…
Une partie de ce traitement ce fait en amont du code ci-dessous, une autre partie en aval.

Pour la partie qui nous intéresse, j'ai retenu l'option suivante :
- Dans le (ou les) fichier(s) texte(s) de base, j'indique la présence d'un champ de fusion en l'encadrant d'accolades {NomDeChamp},
- Je fais en sorte que le nom entre les accolades corresponde exactement au nom du champ de la table dont je veux que le contenu soit utilisé.

J'ai constitué le code ci-après pour qu'Access consulte de lui-même la liste des champs de la table source, vérifie leur présence dans le fichier texte et remplace les champs de fusion par les contenus de la table.

J'ai ajouté des mises en forme pour des champs identifiés date (_DT_) ou heure (_HR_) grâce à leurs noms, pour que les dates soient indiquées au format Vendredi 15 avril 2020 et les heures au format 12h45.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Private sub PersoTxt ()
 
'Merci sajid
'Merci Vladimir Vagaytsev
'https://stackoverflow.com/questions/6360051/access-move-to-next-record-until-eof
 
    Dim k, rcount As Integer
    'Declare some object variables
    Dim dbLib As Database
    Dim rsTable1 As Recordset
    'Set dbLib to the current database (i.e. LIBRARY)
    Set dbLib = CurrentDb
    'Open a recordset object for the Table1 table
    Set rsTable1 = dbLib.OpenRecordset(TblName)
    rcount = rsTable1.RecordCount
        For k = 1 To rcount
        With rsTable1
 
 
'[...] PARTIE EN AMONT
 
 
                    'On liste les champs de la table
                    'Merci Domi2
                    'https://www.developpez.net/forums/d1248880/logiciels/microsoft-office/access/contribuez/lister-nom-champs-table-precise/
 
                   Dim db As DAO.Database
                   Dim tbl As DAO.TableDef '-------------------------> ATTENTION NECCESSITE DE FONCTIONNER SUR UNE TABLE ET PAS UNE REQUETE !!!!!
                   Dim fld As DAO.Field
                   Set db = CurrentDb
                   Set tbl = db.TableDefs(TblName)
                   'On remplace chaque champ de fusion ayant un équivalent dans le texte par le contenu du champ correpondant de la table
                   For Each fld In tbl.Fields
 
                           If InStr(1, fld.Name, "_DT_") = 0 And InStr(1, fld.Name, "_HR_") = 0 Then                   'Si le nom de champ ne contient ni info date, ni info heure, on l'utilise tel quel
                                   TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", rsTable1(fld.Name))
 
                           ElseIf InStr(1, fld.Name, "_DT_") > 0 And InStr(1, fld.Name, "_HR_") = 0 Then               'Si le nom de champ contient une info date, on la met en forme
                                   TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", Format(rsTable1(fld.Name), "dddd d mmmm yyyy"))
 
                           ElseIf InStr(1, fld.Name, "_DT_") = 0 And InStr(1, fld.Name, "_HR_") > 0 Then               'Si le nom de champ contient une info heure, on la met en forme
                                   TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", Format(rsTable1(fld.Name), "h") & "h" & Format(rsTable1(fld.Name), "nn"))
 
                           End If
 
                   Next fld
                   Set tbl = Nothing
                   Set db = Nothing
 
 
   '[...]   PARTIE EN AVAL
 
 
                '-- Go to Next Record ---
             rsTable1.MoveNext
             End With
        Next
        Set rsTable1 = rsTable1
 
End Sub
L'intérêt est de pouvoir conjointement personnaliser la ou les tables sources en rajoutant simplement un champ si nécessaire et le nom de ce même champ dans le message type.
De même, si l'on souhaite utiliser le contenu d'un champ dont on connait l'existence et le nom, il suffit de le rajouter dans le message type, le tout avec un simple éditeur de texte.
Grace à ce mode de gestion, pas besoin de bidouiller dans Access pour créer de nouveaux messages types, avec de nouveaux contenus, ou des personnalisation différentes, même à partir d'une même table.
Au moment de la fusion, Access repèrera toutes les similitudes et fera les modifications.

Tout fonctionne très bien :
- Si je crée un message type avec un champ ne se trouvant pas dans la table, il reste à l'état de {ChampInconnu} dans le texte final,
- Si j'ai des champs de la table non utilisés dans le message type, leurs données ne sont pas utilisées,
- Si un champ date ou heure n'est pas rempli dans la table pour un enregistrement, alors qu'il est présent dans le message type alors la mention {Champ_DT_} ou {Champ_HR_} est supprimée du nouveau message et remplacée par rien (équivalent "").

Une table source, avec tous les champs remplis pour tous les enregistrements, permet de générer très rapidement des centaines de documents textes personnalisés.

"tous les champs remplis pour tous les enregistrements"...

Car il y a un problème.

Quand un champ autre que date ou heure n'est pas rempli pour un enregistrement, ça cause une erreur, que le champ soit utilisé dans le message type ou non :

Erreur d'éxécution '94':
Utilisation incorrecte de Null
Code : Sélectionner tout - Visualiser dans une fenêtre à part
TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", rsTable1(fld.Name))
Surligné en jaune

Si je passe le pointeur sur la ligne surlignée, j'ai des données qui s'affichent :
TxtMsgTmp : affiche bien le contenu du message
fld.Name : affiche bien le nom du champ qui n'est pas rempli pour cet enregistrement
rsTable1 : affiche rsTable1(fld.Name) = Null
fld.Name : affiche bien le nom du champ qui n'est pas rempli pour cet enregistrement

J'ai vérifié que le code suivant fonctionnait est c'est le cas
Code : Sélectionner tout - Visualiser dans une fenêtre à part
TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", "")
J'ai fait plusieurs essais et j'ai découvert que l'utilisation de la fonction Format() empêchait ce problème.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", Format(rsTable1(fld.Name), "dddd d mmmm yyyy"))
'[...]
TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", Format(rsTable1(fld.Name), "h") & "h" & Format(rsTable1(fld.Name), "nn"))
Les champs heures et dates ne causent pas d'erreur quand ils sont vides pour un enregistrement.
J'ai donc tenté :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
TxtMsgTmp = Replace(TxtMsgTmp, "{" & fld.Name & "}", Format(rsTable1(fld.Name)))
Et ça marche.

Même quand un champ n'est pas rempli pour un enregistrement, le texte est modifié par "" et il n'y a pas d'erreur d'exécution.

Mais le problème, c'est que ce formatage abime une partie des données (notamment les n° de téléphone et les codes postaux qui commencent par zéro, car ce dernier est supprimé).

Je pourrais bien entendu ajouter suffisamment de code pour détecter qu'un champ ne se trouve pas dans le message type, ou que son contenu est vide.
Il faudrait pour cela que je sache quoi faire pour ne pas en même temps enrayer le travail déjà accompli.
Car l'ensemble offre déjà une complexité que je maitrise à peine et une vélocité très satisfaisante au regard de la gymnastique accomplie pour chaque message.

De plus, il me semble qu'il ne manque que quelques caractères de codes pour que ça fonctionne en l'état.

J'ai donc trois problèmes :

- Pourquoi un champ dont le contenu est vide pose-t-il un problème, y compris quand il n'est pas présent dans le texte à remplacer ?
- Pourquoi l'usage de format supprime (Ou masque efficacement) le problème ?
- Existe-t-il un usage de Format() qui permettrait d'indiquer que l'on souhaite reprendre le format initial (Format "Neutre", ou Format ""), que le champ soit numérique, date, texte ou autre ?

D'avance merci.