Blog de développement

Interroger l'Annuaire Chorus, sans se compliquer la vie

Dernière Modification le :
2024-02-19
image

La page explicative de l'annuaire des Structures utilisant Chorus Pro est ici. L’annuaire des structures publiques permet d’identifier les données qui doivent obligatoirement être renseignées pour chaque structure publique lors de la transmission de vos factures. La page pour interroger l'Annuaire, en mode portail, est ici. Voir aussi ce guide pratique.

On aura aussi besoin de la description de variables dans Chorus Lire → (elle est aussi dans le ZIP de ressources)

Pourquoi interroger l'annuaire Chorus ?

L'obligation de déposer ses factures sur Chorus est une catastrophe en terme de productivité pour les (petits) fournisseurs et les petites commandes! Or, le dépot sur Chorus est parfaitement automatisable, sans avoir recours à des services payants. Une fois qu'on sait automatiser, on a tout interêt à une saisie sans faute des paramètres du client: Identifiant et Code Service. D'autant que les instructions ne sont pas toujours bien claires sur les bons de commandes, voire complétement absentes (ex. IFPEN)

Un processus automatique, fluide a tout à gagner à valider ces informations critiques, que l'on trouve dans l'Annuaire Chorus. Et il faut que cela soit très rapide et automatisable...

Interroger en local, automatiquement et rapidement

A la main, c'est fastidieux: il faut automatiser. C'est possible avec l'API, mais l'obtention de la clé de développeur est d'une complexité ! Alors que je ne souhaite utiliser que 2 fonctions: savoir si le client exige Chorus et y déposer ma facture...

On pourrait interroger la page avec un simple requete GET dont la forme serait:
https://communaute.chorus-pro.gouv.fr/annuaire-cpro/?nompage=11000201100044&envoyer=
Là encore, ce serait fastidieux...

Pour l'automatisation, sans avoir recours à l'API, c'est simple: on va télécharger l'annuaire, régulièrement et automatiquement et le conditionner pour une interrogation sur une simple liste en format texte

Télécharger les listes simples depuis mon archive

Je vais expliquer plus avant comment faire sans dépendre de personne. En fait, j'archive les 2 listes régulièrement, sous forme simple. Il suffit de télécharger mon archive régulièrement, par exemple chaque Mardi, pour être à jour, sans effort.

Voici la commande CURL. Mieux vaut installer la toute dernière version, disponible ici: https://curl.se/docs/manpage.html, et installer 7Zip depuis son site officiel:

					    
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject("WScript.Shell") '// https://renenyffenegger.ch/notes/Windows/development/COM/Useful-object-libraries/Shell-Automation-Service/index

sNomdePC = "MonPC"		' // a personaliser
sDossierDocuments = "C:\Users\" & sNomdePC & "\Documents\"
sDossierAnnuaire = sDossierDocuments & "Chorus-Annuaire\"
sAnnuaireZIPFilePath = sDossierAnnuaire & "Chorus-Annuaire.zip"
sURL = "http://www.granuloshop.com/Chorus-Annuaire.zip"
if NOT ObjFSO.FolderExists(sDossierAnnuaire) then ObjFSO.CreateFolder sDossierAnnuaire

sCommand = """curl.exe"" -k -L -o """ & sAnnuaireZIPFilePath & """  --url """ & sUrl & """ "
oShell.Run sCommand, 0, True

Fréquence de téléchargement

Chorus est mis à jour, éventuellement, en début de semaine. Parfois, plus tard. On active le script le Mardi. Une fois le téléchargement fait, on ne fit rien jusqu'au prochain Mardi.

Is_Needs_Chorus_Update = False
if NOT objFSO.FileExists (sAccueilFilePath) then Is_Needs_Chorus_Update = true
Set objOpen = objFSO.OpenTextFile(sProchaineMAJChorusFilePath, ForReading, true)
if NOT objOpen.AtEndOfStream then sDateProchaineMAJChorus = objOpen.ReadLine
objOpen.Close : Set objOpen = Nothing
if sDateProchaineMAJChorus = "" then sDateProchaineMAJChorus = "20000101"
if ToYYYYMMDDDate(date()) >= sDateProchaineMAJChorus then Is_Needs_Chorus_Update = true	

' // Puis en fin de processus, armer pour un téléchargement au prochain Mardi (ou Lundi?)
ProchainMardi = DateAdd("d", 2, DateVBS)
Do Until Weekday(ProchainMardi, 1) = 3
	ProchainMardi = DateAdd("d", 1, ProchainMardi)
Loop
sDateProchaineMAJChorus = ToYYYYMMDDDate (ProchainMardi)
Set objOpen = objFSO.OpenTextFile(sProchaineMAJChorusFilePath, ForWriting, true)
objOpen.Write sDateProchaineMAJChorus
objOpen.Close : Set objOpen = Nothing

Public Function ToYYYYMMDDDate(datetime)
	ToYYYYMMDDDate = CStr(Year(datetime))  & StrN2(Month(datetime))  & StrN2(Day(datetime))
End Function 
Private Function StrN2(n)
	If Len(CStr(n)) < 2 Then StrN2 = "0" & n Else StrN2 = n
End Function

Télécharger l'annuaire

L'annuaire Chorus est mis à jour régulièrement, le Lundi (sauf exception, comme un jour férié): on va donc télécharger l'annuaire, en entier, à ce même rythme.

On télécharge la page d'acceuil pour déterminer le nom du fichier .xlsx à télécharger

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")

sAcceuilFilePath = "C:\Users\Moi\Documents\" & "Acceuil.htm"	' // a personaliser
sURL = "https://communaute.chorus-pro.gouv.fr/annuaire-cpro/?"

objWinHttp.open "GET", sURL, False
objWinHttp.send ""
objWinHttp.waitForResponse
If objWinHttp.Status = 200 Then 
	result = objWinHttp.responseText
	' // on cherche la date de derniere mise à jour qui est dans le nom du fichier XLS. Par exemple:
	' // https://communaute.chorus-pro.gouv.fr/wp-content/uploads/2022/12/AIFE-Chorus-Pro-Annuaire_20221219.xlsx
Else
	'  // MsgBox "objWinHttp.Status a une erreur: " & objWinHttp.Status  '// pour debugger
End If

Set objOpen = objFSO.OpenTextFile(sAcceuilFilePath, 2)   ' // ForWriting = 2
objOpen.Write result
objOpen.Close
Set objOpen = Nothing

Déterminer l'URL de l'annuaire à télécharger

On utilise ici un outil peu courant mais assez intuitif: MSHTML. On cherche juste à déterminer l'URL de l'annuaire le plus récent.

Set html = CreateObject("htmlfile")   ' // MSHTML reference: https://tinyurl.com/GRA-HTML-REF
If InStr(result, "charger XLS")> 0 then   ' // la reponse contient telecharger XLS
	html.open
	html.close
	html.body.innerHTML = result
	Set objResult = html.GetElementsByTagName("input")  ' // cree une collection avec tous les tags input
	For i= 0 to objResult.Length - 1
		if InStr (objResult(i).Attributes.getNamedItem("value").value,  "charger XLS") > 0 then
			sURL = objResult(i).Attributes.getNamedItem("onClick").value
			sURL = replace (sURL, "window.location.href='", "")
			sURL = replace (sURL, "'", "")
			i = objResult.Length	' // on a ce que l'on cherchait, donc on sort...
		End If 	
	Next
End If  	' // J'ai fini la determination de URL

On télécharge alors l'annuaire

Il nous reste à télécharger l'annuaire sous la forme de son fichier .xlsx ; bien que le format .xlsx soit de type texte, il semble qu'il faille enregistrer comme Binary

					    					
If sURL <> "" then 
	pos = InstrRev (sURL, "/")
	sFileName = right(sURL, Len (sURL) - pos + 0) ' // on determine le nom du fichier .xlsx
	sPath= sDossierAnnuaire & sFileName
	If NOT objFSO.FileExists (sPath) then 
		objWinHttp.open "GET", sURL,  true   'le troisième paramètre spécifie que WinHTTP soit exécuté de manière asynchrone.
		objWinHttp.send ""
		objWinHttp.waitForResponse
		If objWinHttp.Status = 200 Then 
			SaveBinaryData sPath, objWinHttp.responseBody	' // Save the Binary data to Disk
			Do until objFSO.FileExists (sPath) = True
				WScript.Sleep 100
			Loop
		Else
			'  // MsgBox "objWinHttp.Status a une erreur: " & objWinHttp.Status  '// pour debugger
		End If
	End If
End If

Function SaveBinaryData(FileName, Data)
	Const adTypeText = 1	' // adTypeText for binary = 1
	Const adSaveCreateOverWrite = 2	
	Dim BinaryStream
	Set BinaryStream = CreateObject("ADODB.Stream") '  // ' Create Stream object	
	BinaryStream.Type = adTypeText ' // Specify stream type - we want To save Data/string data.	
	BinaryStream.Open		' // Open the stream And write binary data To the object
	BinaryStream.Write Data	
	BinaryStream.SaveToFile FileName, adSaveCreateOverWrite	' // Save binary data To disk
End Function

				    

Avec Curl, c'est encore plus simple...

					    	
sCommand = """curl.exe"" -k -L -o """ & sPath & """  --url """ & sUrl & """ "	
ReturnError = oShell.Run (sCommand, 0, True)
	' // if ReturnError <> 0 then MsgBox ReturnError  '// pour debugger
						
				    

Convertir le fichier Excel pour une consultation rapide

On dispose du fichier localement, sans plus dépendre d'Internet, sans compte API. Il y a une stratégie à adopter pour pouvoir consulter aisément l'annuaire ainsi téléchargé.

Le fichier Excel ainsi téléchargé est assez gros: 27 Mb. Il contient les Structures, et pour celles qui sont subdivisées en Services, la liste des Services. Chorus gère 200.000 structures! et env. 80.000 Services...
Le fichier contient beaucoup de champs qui ne nous interessent pas trop: on veut juste savoir si le client est inscrit dans Chorus et, si oui, quels sont ses services. Ainsi on pourra éviter des erreurs lors de la saisie de commande et de facture. 27 Mb, c'est lourd! Ca charge difficilement, alors qu'un simple petit fichier texte nous suffirait.

Stratégies possibles de transformation de l'annuaire:
  • - Entrée en base de données classique (MSSQL, Access, LibreOffice Base, ...)
  • - Driver permettant la connexion directe à Excel
  • - Extraction de 2 fichiers CSV, simples et légers. Lire →