Créer une interface entre un processus industriel et les services techniques avec Access

Claude Leloup

Cet article constitue la synthèse de cette discussion sur le forum Access.

Contexte de l'application

On dispose d'une multitude d'enregistrements de format CSV (dix millions par jour !). Si une anomalie survient dans le déroulement du processus industriel, le service technique doit analyser ces données.

L'application consiste à présenter un formulaire qui permet de sélectionner parmi la masse, un échantillon d'enregistrements CSV.

Ces enregistrements sont alors reformatés et exportés dans une feuille Excel qui en fera des graphiques.

Les techniques Access abordées

- créer un modèle d'importation de données ;

- importer tous les fichiers contenus dans un répertoire et ses sous-répertoires éventuels ;

- réexporter dans un fichier Excel, une sélection de ces enregistrements ;

- déclencher par le code une capture d'écran de la fenêtre Access et l'intégrer dans ce fichier Excel.

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum Commentez

- Titre : Créer une interface entre un processus industriel et les services techniques avec Access

- Auteur : Claude Leloup

- Parution : 15 mars 2015

- Licence : 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 ® 2015 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.

Sommaire

- I. Prérequis

- II. Le contexte

- III. Cahier des charges de l'application Access

- III-A. Comment exprimer le choix des enregistrements CSV à extraire

- III-B. Extraction des enregistrements choisis

- IV. Détail de la programmation

- IV-A. La table tInput

- IV-B. Le formulaire fCommande

- IV-C. Une requête « rOutputExcel »

- IV-D. Définition d'un modèle d'importation

- IV-E. Une routine pour importer tous les fichiers d'un répertoire et de ses sous-répertoires éventuels

- V. Le code associé au formulaire

- V-A. Une fonction qui indique si au moins une case a été cochée sur une ligne

- V-B. Le clic sur le bouton « Load »

- V-B-1. Vérifier la cohérence de la demande

- V-B-2. Charger les fichiers

- V-B-3. Construire la requête pour exporter vers Excel

- V-B-3-a. On commence par déterminer l'instant de début et l'instant de fin des enregistrements à exporter

- V-B-3-b. On modifie à la volée le SQL de la requête « rOutputExcel »

- V-B-4. Exporter vers Excel, émettre un message de bonne fin et purger tInput

- VI. Une fonction pour prendre une capture de la fenêtre ACCESS

- VII. Une fonction pour insérer l'image capturée dans le fichier Excel

- VIII. Téléchargement

- IX. Remerciements

I - Prérequis

Ce tutoriel s'adresse à des utilisateurs qui maitrisent déjà les bases du logiciel Access.

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.

II - Le contexte

Un processus industriel livre à titre de contrôle, une série d'enregistrements sous la forme de fichiers CSV (Comma-separated values).

Fichier CSV

La première ligne renseigne le nom des colonnes séparées entre elles par un délimiteur (dans notre cas, un point-virgule).

Les lignes suivantes énumèrent les valeurs de chaque enregistrement.

Pour fixer les idées, en 24 heures, chacune des huit machines (K1, K2…) produit environ 1,2 million d'enregistrements rangés dans différents répertoires.

Quand une anomalie survient, on voudrait extraire de cette masse d'enregistrements, ceux qui méritent d'être analysés pour comprendre ce qui s'est mal passé.

L'idée est de créer un formulaire Access qui va permettre à l'utilisateur de cibler les enregistrements utiles pour les extraire de la masse et alimenter une feuille Excel qui les traduira en graphiques :

Notre propos est de décrire le fonctionnement de l'interface Access.

III - Cahier des charges de l'application Access

À l'aide de quelques clics dans un formulaire, permettre à l'utilisateur de cibler les enregistrements utiles pour les transformer en graphiques.

III-A - Comment exprimer le choix des enregistrements CSV à extraire

N.B. Pour les points 2 à 7, plusieurs cases peuvent être cochées simultanément.

Le choix d'une (et une seule) machine est obligatoire.

L'indication de la position précise une notion qui sera exploitée dans des lignes suivantes (3, 5 et 9).

Cette ligne va de pair avec la ligne 2 :
- dans un 1er temps, on charge tous les fichiers des répertoires « Temperature_x », où « x » correspond à la lettre de la position :

- dans un 2e temps, on élimine les enregistrements correspondant à des thermocouples dont la case n'est pas cochée.

On charge tous les fichiers des répertoires « Temperature Furnance n », où « n » correspond à la case cochée :

Cette ligne va de pair avec la ligne 2 :

- on charge les fichiers contenus dans les répertoires « Logs Burners x », où « x » représente la position choisie à la ligne 2 :

- dans ces enregistrements, on ne retient que ceux qui ont une valeur 100 pour les low_flame et 200 pour les high_flame.

On charge tous les fichiers du répertoire « Outside Burner »

mais on ne retient que les enregistrements qui concernent les cases cochées.

On charge tous les fichiers « Tilting_x » où « x » correspond à la case cochée :

et Ces deux choix sont mutuellement exclusifs : ou bien l'utilisateur spécifie un laps de temps, ou bien il donne une plage de N° de pièces.
Dans cette deuxième hypothèse, le programme va rechercher dans les fichiers des répertoires « Counter x », où « x » correspond aux positions cochées à la ligne 2, les instants qui bornent l'intervalle de temps :

III-B - Extraction des enregistrements choisis

Un clic sur le bouton « Load ».

Le programme balaie alors l'ensemble des répertoires pour importer dans la table « tInput » les enregistrements des fichiers *.csv ad hoc qui correspondent aux choix exprimés.

IV - Détail de la programmation

La db ne contient que trois objets :

IV-A - La table tInput

Elle est vidangée à chaque exécution. Elle sert à recueillir les enregistrements importés des fichiers *.csv.

Les colonnes « Variable », « Temps » et « Valeur » correspondent respectivement aux colonnes « VarName », « TimeString » et « VarValue » des fichiers *.csv.

« TempsFormate » est la mise en forme type Date de « Temps » (texte).

IV-B - Le formulaire fCommande

Il a été présenté au chapitre précédent.

Le code associé à ce formulaire est détaillé plus loin dans cet article.

IV-C - Une requête « rOutputExcel »

Son SQL est régénéré à la volée lors de chaque exécution.

IV-D - Définition d'un modèle d'importation

Dans la barre des menus : Fichier>Données externes>Importer

S'ouvre alors une fenêtre qui permet de désigner le fichier.

On choisit n'importe lequel : ils ont tous la même structure :

Vient alors cette fenêtre :

On clique sur Avancé…

Ce qui permet d'établir la concordance du nom des colonnes avec la table « tInput » et ne prendre que les colonnes utiles et un clic sur « Enregistrer sous… »

IV-E - Une routine pour importer tous les fichiers d'un répertoire et de ses sous-répertoires éventuels

1. Option Compare Database

2. Option Explicit

3. Public Sub ImportCSV(Racine As String)

4. On Error GoTo GestionErreurs

5. Dim FSO As Scripting.FileSystemObject

6. Dim sRep As Scripting.Folder

7. Dim sSubRep As Scripting.Folder

8. Dim sFichier As Scripting.File

9. Set FSO = New Scripting.FileSystemObject

10. Set sRep = FSO.GetFolder(Racine)

11. 'Boucle sur les fichiers

12. For Each sFichier In sRep.Files

13. 'Importer

14. DoCmd.TransferText acImportDelim, "ImportModele", "tInput", sFichier.Path, True

15. Next sFichier

16. 'Récursivité pour les sous-répertoires

17. For Each sSubRep In sRep.SubFolders

18. ImportCSV sSubRep.Path

19. Next sSubRep

20. 'Libérer

21. Set sRep = Nothing

22. Set FSO = Nothing

23. Exit Sub

24. GestionErreurs:

25. Select Case Err.Number

26. Case 76

27. MsgBox "Le répertoire " & Racine & " est absent !", vbCritical

28. Case Else

29. MsgBox "Erreur N° " & Err.Number & " " & Err.Description & vbLf _

30. & "dans ImportCSV().", vbCritical

31. End Select

33. End Sub

Explication du code (les nombres en gras indiquent les N° de lignes)

Le paramètre « Racine » est le chemin d'un répertoire (encadré de doubles- quotes « " »).

Exemple d'appel : Call ImportCSV( "C:\ApplicationAccess\FichiersCsv").

5-8 : la définition de ce type de variables nécessite d'installer la bibliothèque Microsoft Scripting Runtime

10 : on se place dans le répertoire donné en paramètre.

12-15 : on boucle sur chaque fichier contenu dans ce répertoire. Ligne 14, on importe le fichier :

D'une manière générale, pour se documenter sur la signification d'une partie de code :
- placer le curseur de la souris à l'intérieur d'un mot-clé ;
- enfoncer la touche <F1>.
L'aide Access s'ouvre à la bonne page.

16-19 : quand on a traité tous les fichiers de ce répertoire, on regarde si ce dernier contient lui-même un ou plusieurs sous-répertoires. Si c'est le cas, la sub appelle une sub (elle-même !) avec comme paramètre ce sous-répertoire (c'est ce qu'on appelle la récursivité)… et ainsi de suite jusqu'à épuisement de l'arborescence.

20-23 : on sort, proprement.

24-32 : s'il s'avérait qu'un répertoire manque dans l'arborescence, Access lèverait une erreur N° 76

En 26-27, on trappe cette erreur et on affiche ce message, plus explicite, pour informer l'utilisateur :

V - Le code associé au formulaire

V-A - Une fonction qui indique si au moins une case a été cochée sur une ligne

1. Public Function LignePresente(Ligne As String) As Boolean

2. Dim ctl As Control

3. For Each ctl In Me.Controls

4. If ctl.Name Like Ligne & "*" Then

5. If ctl = -1 Then

6. LignePresente = True

7. Exit Function

8. End If

9. End If

10. Next ctl

11. End Function

Explication du code

Le paramètre correspond au sigle d'une ligne de cases à cocher.

2 : on définit un objet contrôle. Ce choix implique le chargement de la bibliothèque Microsoft DAO x.x Library

3-10 : on parcourt la collection des contrôles du formulaire et si celui-ci porte un nom correspondant à une ligne (ex. : ccPosA pour case à cocher PosA) et que cette case est cochée (le contrôle vaut -1), alors la fonction renvoie True. Si aucune case de ce type n'est cochée, alors la fonction renvoie False.

V-B - Le clic sur le bouton « Load »

1. Option Compare Database

2. Option Explicit

4. Dim sMachine As String

8. Private Sub btLoad_Click()

9. Dim ctl As Control

10. Dim i As Integer

11. Dim sSql As String

12. Dim sDebut As Variant

13. Dim sFin As Variant

14. Dim q As QueryDef

17. '-----------------------------------

18. 'vérifier la cohérence de la demande

19. '-----------------------------------

21. 'Une date de départ ou une date de pièce doit être spécifiée

22. If IsNull(Me.txtDateDepart) + IsNull(Me.txtDateNum) <> -1 Then

23. MsgBox "Vous devez choisir soit par date soit par pièces.", vbCritical

24. Exit Sub

25. End If

27. 'Si recherche par pièces, alors les 3 paramètres doivent être spécifiés

28. If IsNull(Me.txtDateNum) = False Then

29. If IsNull(Me.txtNumDepart) + IsNull(Me.txtNumArrivee) <> 0 Then

30. MsgBox "Un des paramètres manque pour la recherche par pièces.", vbCritical

31. Exit Sub

32. End If

33. End If

35. 'Une machine doit être sélectionnée

36. If Me.CxMachine = 0 Then

37. MsgBox "Vous devez choisir un carrousel", vbCritical

38. Exit Sub

39. End If

41. 'Si Thermocouple ou Burners est sélectionné, au moins une position doit l'être également

42. For Each ctl In Me.Controls

43. If ctl.Name Like "ccBur*" Or ctl.Name Like "ccThe*" Then

44. If ctl = -1 Then

45. If LignePresente("ccPos") = False Then

46. MsgBox "Vous avez choisi Burners ou Thermocouple => vous devez choisir au moins une position.", vbCritical

47. Exit Sub

48. End If

49. End If

50. End If

51. Next ctl

53. 'Si la demande porte sur une plage de N°, le départ et l'arrivée doivent être mentionnés ensemble

54. If IsNull(Me.txtNumDepart) And Not IsNull(Me.txtNumArrivee) Then

55. MsgBox "Le numéro de pièce de départ manque.", vbCritical

56. Exit Sub

57. End If

58. If Not IsNull(Me.txtNumDepart) And IsNull(Me.txtNumArrivee) Then

59. MsgBox "Le numéro de pièce d'arrivée manque.", vbCritical

60. Exit Sub

61. End If

62. If Not IsNull(Me.txtNumDepart) And Me.txtNumArrivee < Me.txtNumDepart Then

63. MsgBox "Les numéros de pièce sont incohérents.", vbCritical

64. Exit Sub

65. End If

67. 'Si la demande porte sur une plage de N°, au moins une position doit être sélectionnée

68. If Not IsNull(Me.txtNumDepart) And LignePresente("ccPos") = False Then

69. MsgBox "Pour une plage de N°, une position doit être mentionnée.", vbCritical

70. Exit Sub

71. End If

74. '--------------------

75. 'Charger les fichiers

76. '--------------------

77. DoCmd.SetWarnings False

78. 'Purger tInput

79. DoCmd.RunSQL "DELETE Variable FROM tInput;"

81. 'Ligne Position

82. If LignePresente("ccPos") = True Then

83. 'Charger Compteur* si plage de N° de pièces

84. If Not IsNull(Me.txtNumDepart) Then

85. For i = 65 To 70

86. '1° on charge les compteurs

87. If Me("ccPos" & Chr(i)) = True Then

88. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Counter " & Chr(i))

89. End If

90. Next i

91. '2° seuls les enregistrements de la date saisie et avec [Valeur]<> 0 nous intéressent

92. DoCmd.RunSQL "DELETE Valeur FROM tInput WHERE Valeur=0;"

93. 'N.B. on éliminera ceux d'autres dates plus bas dans ce code (quand Temps aura été formaté)

94. End If

95. End If

97. 'Ligne Thermocouple

98. If LignePresente("ccThe") = True Then

99. '1° charger Les Temperature* des positions

100. For i = 65 To 70

101. If Me("ccPos" & Chr(i)) = True Then

102. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Temperature " & Chr(i))

103. End If

104. Next i

105. '2° éliminer les enregistrements de type B?_TEMPER_TCn pour les TCn non cochés

106. For i = 1 To 8

107. If Me("ccThe" & i) = False Then

108. DoCmd.RunSQL "DELETE Variable FROM tInput WHERE Variable Like ""B?_TEMPER_TC" & i & """;"

109. End If

110. Next i

111. End If

113. 'Ligne Furnace

114. 'Charger les fichiers Temperature Furnance n

115. If Me.ccFur1 = True Then

116. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Temperature Furnance 1")

117. End If

118. If Me.ccFur2 = True Then

119. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Temperature Furnance 2")

120. End If

122. 'Ligne Burners

123. If Me.ccBurH = True Or Me.ccBurL = True Then

124. '1° charger les fichiers Logs Burners correspondant aux positions choisies

125. For i = 65 To 70 'chr(65) = A

126. If Me("ccPos" & Chr(i)) = True Then

127. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Logs Burners " & Chr(i))

128. End If

129. Next i

130. '2° ne retenir que les valeurs 100 pour low flame et 200 pour high flame

131. sSql = "DELETE tInput.tImputPK, tInput.Variable " _

132. & "FROM tInput " _

133. & "WHERE (((tInput.tImputPK) " _

134. & "Not In (SELECT tImputPK FROM tInput " _

135. & "WHERE ((Variable Like ""*_position_burner_low_flame"") " _

136. & "AND (Valeur=100)) " _

137. & "OR ((Variable Like ""*_position_burner_high_flame"") " _

138. & "AND (Valeur=200));)) " _

139. & "AND ((tInput.Variable) Like ""BURNERS_FLAME\?_position_burner_*""));"

140. DoCmd.RunSQL sSql

141. '3° supprimer Flame non choisies

142. If Me.ccBurH = False Then

143. DoCmd.RunSQL "DELETE Variable FROM tInput WHERE Variable Like ""*_position_burner_high_flame"";"

144. End If

145. If Me.ccBurL = False Then

146. DoCmd.RunSQL "DELETE Variable FROM tInput WHERE Variable Like ""*_position_burner_low_flame"";"

147. End If

148. End If

150. 'Ligne Outside_Burner

151. If LignePresente("ccOut") = True Then

152. '1° Charger tout le répertoire Outside_Burner

153. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Outside_Burner")

154. '2° éliminer les enregistrements de type Outside_Burner_TCn pour les TCn non cochés

155. For i = 1 To 8

156. If Me("ccOut" & i) = False Then

157. DoCmd.RunSQL "DELETE Variable FROM tInput WHERE Variable Like ""Outside_Burner_TC" & i & """;"

158. End If

159. Next i

160. End If

162. 'Ligne Tilting

163. If LignePresente("ccTil") Then

164. '1° charger les répertoires Tilting_lettre correspondant

165. For i = 65 To 70 'chr(65) = A

166. If Me("ccTil" & Chr(i)) = True Then

167. Call ImportCSV(CurrentProject.Path & "\FichiersCsv\" & sMachine & "\Tilting_" & Chr(i))

168. End If

170. Next i

171. End If

173. 'Toilettage final

174. 'Supprimer les enregistrements Like "$RT*"

175. DoCmd.RunSQL "DELETE Variable FROM tInput WHERE Variable Like ""$RT*"";"

176. 'Formater la donnée temps

177. DoCmd.RunSQL "UPDATE tInput SET TempsFormate = Replace([temps],""."",""/"");"

179. '-----------------------------------------

180. 'Construire ce qui sera exporté vers Excel

181. '-----------------------------------------

183. 'Déterminer le laps de temps (Début et fin)

184. If IsNull(Me.txtNumDepart) Then

185. sDebut = "#" & Format([Forms]![fCommande]![txtDateDepart], "mm/dd/yy") & " " _

186. & Nz([Forms]![fCommande]![txtHeureDepart], "00:00:00") & "#"

188. sFin = "#" & Format([Forms]![fCommande]![TxtDateArrivee], "mm/dd/yy") & " " _

189. & Nz([Forms]![fCommande]![txtHeureArrivee], "23:59:59") & "#"

190. Else 'donc si l'utilisateur a mentionné des N° de pièces

191. 'D'abord éliminer les COUNTER_* d'une autre date que celle mentionnée

192. DoCmd.RunSQL "DELETE Variable, Format([TempsFormate],""mm/dd/yy"") AS Expr1 " _

193. & "FROM tInput " _

194. & "WHERE Variable Like ""COUNTER_*""" _

195. & "AND Format([TempsFormate],""mm/dd/yy"") " _

196. & " <>Format([Forms]![fCommande]![txtDateNum],""mm/dd/yy"");"

197. 'Trouver le laps de temps entre les deux pièces

198. sDebut = Format _

199. (DLookup("TempsFormate", "tinput", "Variable like ""COUNTER_*"" and valeur =" & Me.txtNumDepart), _

200. "mm/dd/yy hh:mm:ss")

201. If Len(sDebut) = 0 Then

202. MsgBox "Le N° de pièce Début n'a pas été trouvé dans la sélection", vbCritical

203. Exit Sub

204. Else

205. sDebut = "#" & sDebut & "#"

206. End If

207. sFin = Format _

208. (DLookup("TempsFormate", "tinput", "Variable like ""COUNTER_*"" and valeur =" & Me.txtNumArrivee), _

209. "mm/dd/yy hh:mm:ss")

210. If Len(sFin) = 0 Then

211. MsgBox "Le N° de pièce Fin n'a pas été trouvé dans la sélection", vbCritical

212. Exit Sub

213. Else

214. sFin = "#" & sFin & "#"

215. End If

216. End If

218. 'Construire la requête à exporter

219. sSql = "SELECT tInput.tImputPK, tInput.Variable, tInput.Valeur, " _

220. & "tInput.TempsFormate AS [Date], Format([Tempsformate],""hh:mm:ss"") AS Heure " _

221. & "FROM tInput " _

222. & "WHERE (((tInput.TempsFormate)>=" & sDebut & " And (tInput.TempsFormate)<=" & sFin & "));"

223. Set q = CurrentDb.QueryDefs("rOutputExcel")

224. q.SQL = sSql

225. Set q = Nothing

227. 'Export vers FromAccess.xls

228. Kill CurrentProject.Path & "\FromAccess.xls"

229. DoCmd.TransferSpreadsheet acExport, , "rOutputExcel", CurrentProject.Path & "\FromAccess.xls", False

231. 'Ajout de la capture d'écran

232. Call AltPrintScreen

233. Call AjoutPrintScreen

235. 'Message de bonne arrivée

236. MsgBox DCount("*", "rOutputExcel") & " enregistrements ont été inclus dans FromAccess.xls", vbInformation

237. 'Purger tInput

238. DoCmd.RunSQL "DELETE Variable FROM tInput;"

239. DoCmd.SetWarnings True

240. End Sub

Remarquez que la variable sMachine est définie en tête du module de classe du formulaire.

Elle est donc visible tout le temps que le formulaire reste ouvert.

Le code se compose de trois parties :

V-B-1 - Vérifier la cohérence de la demande

Instructions de 21 à 71.

Même si le VBA ne vous est pas (encore) familier, les commentaires inclus dans le code sont suffisamment explicites pour que vous « sentiez » ce qui s'y passe.

Quelques compléments

IsNull(Me.NomUnControle) vaut -1 si le contrôle est Null et 0 si le champ est complété ..

Me.NomCase vaut -1 si la case est cochée et 0 si elle ne l'est pas.

V-B-2 - Charger les fichiers

Instructions de 77 à 177.

Ici aussi, les commentaires insérés dans le code devraient aider votre compréhension.

Quelques compléments

- La fonction Chr(Nombre) renvoie le caractère ASCII qui correspond au nombre indiqué.

Par exemple :

For i = 65 To 70
Debug.print Chr(i))
Next i

affichera successivement A, B, C, D, E et F dans la fenêtre d'exécution.

- À beaucoup d'endroits dans le code, on exécute des requêtes créées à la volée. Si vous voulez les visualiser avec l'interface graphique, copier le SQL dans une requête de test.
Pour vous familiariser avec cette technique, voyez ce tutoriel Initiation - Débogage : requêtes écrites par VBA de Charles A (cafeine) et singulièrement, le chapitre V.
Si vous récupérez le SQL particulièrement complexe des instructions 131 à 139 (en ajoutant une instruction Debug.print sSql entre la 139 et la 140) et que vous le collez dans une requête, vous obtiendrez ceci :

V-B-3 - Construire la requête pour exporter vers Excel

Ce sont les instructions 183-fin.

V-B-3-a - On commence par déterminer l'instant de début et l'instant de fin des enregistrements à exporter

De deux choses, l'une

- ou bien l'utilisateur a choisi de limiter en encodant les deux instants :

- ou bien, il a spécifié une date et deux numéros de pièce pour exprimer l'intervalle :

S'il a donné les deux instants

on garnit deux variables sDebut et sFin avec le contenu des champs txtDateDepart/txtHeureDepart et txtDateArrivee/txtHeureArrivee du formulaire.
Si txtHeureDepart et/ou txtHeureArrivée sont restées Null, on les remplace respectivement par 00:00:00 et 23:59:59.

S'il a donné des numéros de pièces

On commence par éliminer tous les enregistrements des compteurs qui ne correspondent pas à la date donnée :

On cherche ensuite l'instant qui correspond à la première pièce :

S'il s'avère que l'utilisateur a renseigné une pièce non présente dans les enregistrements importés, un message d'alerte est affiché et le processus s'interrompt.

Idem mutatis mutandis pour la seconde pièce mentionnée :

V-B-3-b - On modifie à la volée le SQL de la requête « rOutputExcel »

V-B-4 - Exporter vers Excel, émettre un message de bonne fin et purger tInput

L'instruction 232 provoque la capture d'écran de la fenêtre Access en cours, donc du formulaire fCommande avec les cases cochées. (L'image est donc logée dans le presse-papier.)

L'instruction 233 appelle la fonction AjoutPrintScreen décrite au paragraphe suivant.

VI - Une fonction pour prendre une capture de la fenêtre ACCESS

Ces quelques lignes de code ont été trouvées sur internet à cette adresse.

Il suffit de les copier-coller telles quelles dans un module (par exemple : « mCaptureEcran »).

Option Compare Database
Option Explicit

'http://word.mvps.org/faqs/macrosvba/PrtSc.htm

Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Const KEYEVENTF_KEYUP = &H2
Private Const VK_SNAPSHOT = &H2C
Private Const VK_MENU = &H12

Sub AltPrintScreen()
keybd_event VK_MENU, 0, 0, 0
keybd_event VK_SNAPSHOT, 0, 0, 0
keybd_event VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0
keybd_event VK_MENU, 0, KEYEVENTF_KEYUP, 0
End Sub

Si vous lancez l'instruction Call AltPrintScreen, l'image de la fenêtre ACCESS est capturée et logée dans le presse-papier.

VII - Une fonction pour insérer l'image capturée dans le fichier Excel

1. Public Sub AjoutPrintScreen()

2. Dim xlApp As Excel.Application

3. Dim xlSheet As Excel.Worksheet

4. Dim xlBook As Excel.Workbook

6. ' Initialiser les variables

7. Set xlApp = CreateObject("Excel.Application")

8. Set xlBook = xlApp.Workbooks.Open(CurrentProject.Path & "\FromAccess.xls")

10. ' Ajouter une feuille de calcul nommée Choices

11. Set xlSheet = xlBook.Worksheets.Add

12. xlSheet.Name = "Choices"

14. ' Coller l'image dans la 1re cellule

15. xlSheet.Cells(1, 1).Select

16. xlApp.ActiveSheet.Paste

18. ' Fermeture

19. xlBook.Close (True)

20. xlApp.Quit

21. Set xlSheet = Nothing

22. Set xlBook = Nothing

23. Set xlApp = Nothing

25. End Sub

Explication du code

2-3 : la définition de ces variables implique d'ajouter la bibliothèque Microsoft Excel xx.x Object Library au projet

Les commentaires inclus dans le code devraient vous permettre de comprendre ce qui s'y passe.

VIII - Téléchargement

L'application test (Access2000) peut être téléchargée ici.

IX - Remerciements

Ma gratitude à :
ludi42 d'avoir expliqué en détail l'aspect métier de cet article ;

jimbolion et madefemere pour leurs contributions pendant la mise au point.

milkoseck pour la relecture orthographique.