Blog de développement

Extraction et Validation du Numéro de TVA

Dernière Modification le :
2024-01-13

On sait récupérer les Mails de Commande (avec notre Listener), convertir les PDF, reste à remplir automatiquement notre base de données...

Des travaux récents abordent la question de l'identification des numéros de TVA par Intelligence Artificielle. Lire → et aussi Lire →
A défaut de pouvoir utiliser cette intelligence là, je me console avec un cartésianisme de raison.

Extraire ou reconnaitre, ce c'est pas valider !

La cause la plus fréquente de numéros de TVA invalides sont de simples erreurs de frappe de la part du client. Pour rendre le processus de commande plus rapide et plus convivial, vous devez utiliser une expression régulière pour valider le format du numéro de TVA immédiatement pendant que le client remplit votre formulaire de commande en ligne.

On trouve sur internet une multitude d' expressions régulières validant le format d'un Numéro de TVA: c'est à dire vérifier qu'il a la bonne structure, le bon format, avant de le soumettre à un algorithme de prévalidation puis au VIES pour interrogation dans une base de données officielle.

Tel n'est pas notre problème!!! Pour rendre les numéros de TVA plus faciles à lire pour les humains, les clients ajoutent souvent une ponctuation supplémentaire pour diviser les chiffres en groupes. Par exemple, un client allemand peut écrire son numéro de TVA DE123456789 sous la forme DE 123.456.789
Cela complique l'écriture d'une expression régulière unique qui corresponde aux numéros de TVA de 27 pays avec n'importe quelle ponctuation possible.

La Commission Européenne (VIES) nous aide un peu... juste un tout petit peu...

Dans sa FAQ, le VIES Accéder au service → , aborde la question qui intéresse tout le monde: (c'est la question Q11): Est-il possible de connaitre les algorithmes utilisés pour former les numéros de TVA ? Et la réponse est... La Commission Européenne ne peut pas divulguer ces algorithmes. WTF !!! Lire →

On n'est pas impressionné par la diligence des fonctionnaires bruxellois!!! Toutefois, la structure des numéros d’identification TVA est indiquée dans leur tableau... C'est bien la moindre des choses!

La liste fournie par la commission Européenne a omis d'inclure le préfixe EU, qui correspond aux entités extra-Européennes ayant demandé un code de service à... l'Administration Européenne ! On ne manquera pas d'ajouter... la Suisse (CH)

La page Wikipedia est la plus complète. Lire →

Une expression régulière attrape-tout

En entrant un candidat mal formé, on expose l'expression régulière du VIES:
The VAT number does not match the following regular expression : "[a-zA-Z0-9+*]*"
C'est trop basique, on va attraper tout sur le Bon de Commande. Il nous faut une expression Régulière qui attrape large, mais pas trop large...
P. Kańkowski propose ici : Prefixe (Country code): [A-Z]{2} et pour le numéro (VAT ID without the country code): [0-9A-Za-z\+\*\.]{2,12}. On pensera à y ajouter le tiret et le point. Mais il y a encore confusion possible avec les Numéros de Commande à Préfixe...

  • Elle commence par les Prefixes Pays
  • Il n'y a que des majuscules
  • Le dernier caractère est un chiffre ou une lettre
  • l'avant dernier caractère est un chiffre ou un V ou S (Suisse) ou un W ou F(Irlande)
  • le précédent (l'antépénultième) est un chiffre ou un I ou T ou W (Suisse) ou un B (Pays-Bas) ou un blanc (Danemark)ou M (Norvège)
  • Les 2 premiers caractères sont des chiffres, sauf pour la France, l'Espagne, l'Autriche (U)
  • Il y a au moins un bloc de 2 chiffres, malgré la ponctuation (En pratique on ne peut pas utiliser cette règle)

Le plus long est le Suisse (CHE 123.456.789 plus MWST/TVA/IVA selon le Canton), le plus court, un GBGD9001 ou GBHA9599, un (Nord-)Irlandais : XIGD9996 ou XIHA9997

En outre, on autorise les caractères de confusion (OCR): BLlIiOoZ qui sont des erreurs courantes pour 8, 1, 0, 2.

On construit une liste de préfixes, En incluant EU, CHE (Suisse), GB (Grance-Bretagne), NO (Norvège), XI (Irlande du Nord), GR, comme alias courant de EL (Grèce)

On construit une liste de TVA a tester

BERLIOZ et l'Expression Régulière Fantastique

Notre méthode d'entrée, c'est l'OCR. Or, un OCR rapide peut générer des erreurs 'classiques' telle que la permutation O-0. La liste Avalara contient d'ailleurs une telle erreur: NL 123456789B01 ou NL 123456789BO2

Si on veut lire 8ER1102, il faut attraper BERLIOZ, dans un premier temps, puis corriger. Les erreurs OCR peuvent être classées en 3 types principaux: Deletion, Insertion et Substitution. La correction lexicale ne semble pas apporter un plus à notre problème. Je ne traite que le cas des substitutions.

Erreurs de confusion: scribenet.com fait un panorama détaillé des erreurs OCR Lire →

les ponctuations: en pratique, outre le blanc, je ne vois que - et . Or, la Commission prévoit + et * ... Jamais vu, mais bon... On en tient compte.

Dans l'écriture ou la modification de l'Expression Régulière "attrape tout", il faut s'assurer de ne jamais avoir de double || . et que \s et l'espace blanc, ce n'est pas pareil. Dans la formule ci-dessous, il y a des blancs!

On attrape donc R0 pour RO, quitte à être plus spécifique après, avec une Expression régulière pour chaque pays, et une vérification dans VIES.

(AT|BE|BG|HR|CY|CZ|DE|DK|EE|EL|FI|FR|GR|HU|IE|IT|LV|LT|LU|MT|NL|NO|PL|PT|RO|SK|SI|SE|ES|CHE|GB|EU|XI|N0|R0|8E|8G|C2|F1|1E|S1|X1)([- ]*)([A-Z0-9lio]{2})([MBLlIiOoZ0-9- \.]{1,12})([MWTBLlIiOoZ0-9 ])([SVBLlIiOoZ0-9])([lio0-9A-Z])

Ensuite... des expressions régulières spécifiques

Nous avons maintenant attrapé notre chaine de caractères, simulant ainsi une frappe au clavier dans la case ad'hoc d'un formulaire, nous extirpons les ponctuations, corrigeons les alias et substitutions communes d'OCR Sans espace ni ponctuation, on peut alors envisager cette expression régulière qui vérifie si le numéro de TVA est valable pour l'un des 27 pays de l'UE, publiée par O'Reilly. Lire →

La suppression de ponctuation est inconditionnelle. La correction minuscules et chiffres trompeurs (lio8210) vers lettre majuscule (IOBZ) se fait là où des lettres majuscules sont attendues (comme dans les préfixes). La correction de lettres et chiffres trompeurs (lioBOZI) vers des chiffres (8210) se fait là où des chiffres sont attendus, selon le format officiel.

Ma référence pour les expressions régulières est regular-expressions.info Vérifier →. Je vérifie sur regexlib.com. Vérifier →

Des chiffres de contrôle dans toute l'Europe

L'inventaire sur Wikipédia est incomplet. Il existe un service de verification, moins utile depuis que VIES s'est imposé: le CHECK-TIN de la Commission Européenne. Vérifier →.Sa spécification technique montre bien que tous les pays ont un algorithme de contrôle pour les TVA professionnelles, sans exception. Lire →
La liste tenue par SAP (IBM) permet de compléter avec d'autres pays... Lire →

Une erreur fréquente est la présence d'un IBAN sur le bon de commande, qui, lui aussi, commence par un préfixe pays (et c'est le même) et est numérique. Certes, la longueur est différente mais parfois pas trop (ex. Belgique)

https://www.iban.com/structure https://github.com/loophp/tin

La clé informatique: France

La particularité ici est qu'il existe(rait) 2 styles: le nouveau et l'ancien. SAP est singulier quant au "nouveau" style (on ne retrouve la règle de calcul nulle part ailleurs...) et le site de la commission ne mentionne même pas "l'ancien", pourtant très largement usité, au point que je n'ai jamais rencontré le "nouveau"! Solaris reprend le concept de Modulo 511: c'est probablement le seul!
Wikipedia ne connait que l'ancien Lire →. Les sites du gouvernement français sont muets sur le sujet!

La situation est ubuesque! Au point de se demander si quelqu'un n'a pas joué un mauvais tour à la Commission et aux programmeurs qui s'en sont inspirés... Il y a probablement confusion entre l'dentifiant de TVA et l'identifiant fiscal, personnel, lui. Ainsi le site de la commission valide mes numéros personnels, mais pas ceux de la société. On connaitra peut être un jour le fin mot de l'histoire

La clé française usuelle extrait le SIREN, (les 9 derniers de 11 chiffes...) et suit la règle suivante :
Clé TVA = [12 + 3 × (SIREN modulo 97)] modulo 97

Pour être complet, il convient de rappeler que le SIREN contient, lui aussi, une clé informatique: Il est composé de neuf chiffres, les huit premiers sont attribués séquentiellement (sauf pour les organismes publics commençant par 1 ou 2), le neuvième est une clé de contrôle (modulo 10, suivant l’algorithme de Luhn).

On aurait officiellement 2 formats: un à 11 chiffres, soit donc 2 chiffres et 9 chiffres (SIREN), et un autre, avec 2 Alphanumériques suivi de 9 chiffres. En tous les cas un schémas 2 + 9... Le Schéma 2 est obscur et mal documenté (si tant est qu'il existe pour de vrai) : on l'autorisera sans chercher à le vérifier autrement que par le VIES. Si on le voit émmerger, on s'en occupera.

Le chiffre de contrôle: Belgique

Les deux derniers chiffres forment le chiffre de contrôle. On utilise la méthode dite 'modulo 97' - méthode qui permet de vérifier si un numéro est correct. Cela signifie en pratique que les deux derniers chiffres sont égaux à la différence entre 97 et le reste de la division des huit premiers chiffres par 97. Exemple
Numéro d'entreprise 0449.386.647
- Division des huit premiers chiffres par 97 : 04 493 866 / 97 = 46 328
Reste de la division = 50
- Différence entre 97 et le reste de la division : 97 - 50 = 47 = les deux derniers chiffres du numéro d'entreprise.
La méthode de contrôle restera applicable pour les numéros de TVA commençant par un chiffre '1' qui seront attribués ultérieurement.

L'apparente similarité avec le système français peut être piégeuse: dans les 2 systèmes, la base est un jeu de 9 chiffres (SIREN en France, BCE, en Belgique), lequel inclut une clé de contrôle, mais... ce n'est pas la même! En France, outre la clé de contrôle DANS le SIREN, on ajoute une clé de contrôle propre à la TVA. Outre Quievrain, non...

Valider via le VIES ? du pour et du contre...

Avec l'apparition (récente...) de l'API REST, la consultation automatique du VIES est vraiment facile. Inutile d'utiliser un service payant, Selenium ou le vieux système SOAP du VIES, rendu obsolète. Cette requête, systématique est encouragée par tous les sites sur le sujet. On estime à 2 milliards par an, le nombre de facture B2B en France. Multiplions par 10 pour un ordre de grandeur Européen et divisons pas 200 jours ouvrables, il faudrait que le VIES soit dimensionné à 100 millions de requêtes par jour, avec une disponibilté totale 24/24, 7j./7j.

Interroger le VIES est simplissime: on sépare le préfixe (sPrefix) du reste (sTin) dans le numéro de TVA intracommunautaire à vérifier, on fait de même pour le sien, et hop!

sURL = "https://ec.europa.eu/taxation_customs/vies/rest-api/ms/" & sPrefix & "/vat/" & sTin & "?requesterMemberStateCode=FR&requesterNumber=81915105977"

Pour réduire les interactions avec le VIES, on peut maintenir une petite base de données, à mettre à jour régulièrement

					    sNumero_TVA_A_Verifier = "FR95327733184" ' // A documenter, ici celui de Microsoft France, pour le fun
sPrefix = "FR" ' // A documenter
sTin = "95327733184" ' // A documenter
sDossierVIES = "C:\Users\MonProfil\Documents\VIES\"		' // A personnaliser
sVIESFileName = sNumero_TVA_A_Verifier & ".txt"		' // on va conserver le résultat ici
sVIESFilePath = sDossierVIES & sVIESFileName
sCurlBatVIESFilePath = "C:\Users\MonProfil\Documents\VIES\MonBatVIES.bat"
If NOT objFSO.FileExists(sVIESFilePath) then objFSO.CreateTextFile sVIESFilePath		'On crée le fichier avant, car le .bat ne le fera pas
	' // on remet le contenu à zéro avant interrogation pour effacer des données anciennes éventuelles
Set objOpen = objFSO.OpenTextFile(sVIESFilePath, ForWriting, true)
objOpen.Write ""
objOpen.Close
Set objOpen = Nothing

	' // Ici, on utilise un tunnel (pipe, visible à cause de | ) pour récupérer l'info et l'identifier d'un seul coup
	' //  On met une limite de temps pour la réponse, ici 10 secondes
sURL = "https://ec.europa.eu/taxation_customs/vies/rest-api/ms/" & sPrefix & "/vat/" & sTin & "?requesterMemberStateCode=FR&requesterNumber=81915105977"
sCommand = """curl.exe"" --max-time 10 --url """ & sURL & """ | find  /i ""isValid"" > """ & sVIESFilePath & """ "

	' // On doit écrire un bat puis l'exécuter, ceci à cause du pipe...
Set objFile = objFSO.OpenTextFile(sCurlBatVIESFilePath, 2,true) 	'2 = ForWriting
objFile.Write sCommand
objFile.Close
Set objFile = nothing

Command = """" & sCurlBatVIESFilePath & """" 
Call Log ("Command: " &  sCommand )
oShell.Run Command, 0, True
Wscript.Sleep 5000

sVIES_Content = ""
sVIES_Valid =  ""

Set objOpen = objFSO.OpenTextFile(sVIESFilePath, 1, true) 	'ForReading = 1
If NOT objOpen.AtEndOfStream then sVIES_Content  = ObjOpen.ReadAll
objOpen.Close
Set objOpen = Nothing

If sVIES_Content <> "" then
	sTVA_VIES_Tested = sNumero_TVA_A_Verifier & VbCrLf & sTVA_VIES_Tested
	If InStr(1, sVIES_Content, "true", 1) then
				'  // sTVA_VIES_Valide = sNumero_TVA_A_Verifier & VbCrLf & sTVA_VIES_Valide  'On peut vouloir conserver une liste de numéros valides
		sVIES_Valid =  "1"
	Else
				' // sTVA_VIES_Non_Valide = sNumero_TVA_A_Verifier & VbCrLf & sTVA_VIES_Non_Valide   'On peut vouloir conserver une liste de numéros non valides
		sVIES_Valid =  "0" 
	End If	
End If	
' // Ensuite, selon la valeur du résultat, on met à jour notre base de données...
				    

XXX

https://ec.europa.eu/taxation_customs/vies/#/technical-information

https://www.valdit.com/developers/vat-number-validation-api/api-documentation/

i le numéro de TVA commence par CH, le numéro de TVA doit compter entre 6 et 20 caractères.

https://help.afas.nl/help/FR/SE/Fin_Config_VatIct_NrChck.htm

https://www.ict-reuse.be/file/6d9dc022f0af78b3c218d23de4ff713f67615d11/42695618f38c5246869b3958c418bb8363548a30/SoftwareReuseCatalogue_SmalsUtils-Validation-func-techn-Analysis.pdf

https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s21.html

https://docshield.kofax.com/KTM/en_US/6.4.0-uuxag78yhr/help/PB/ProjectBuilder/450_Extraction/VendorLocator/t_DefiningtheTaxIDorVATIDInput.html

https://forum.thirtybees.com/topic/2684-vat-module-issues/

https://vatstack.com/articles/norway-vat-number-validation

https://stackoverflow.com/questions/73528499/form-submission-via-rvest-httr-vies-vat-validation

https://www.uid.admin.ch/Detail.aspx?uid_id=CHE-102.822.114

https://www.info-clipper.com/fr/?q=ShowDetails&ellis=1&retry=1&sid=406557959&country_code=CH

curl.exe -d '^^^^^IE^^9825613N^^^^^' "https://ec.europa.eu/taxation_customs/vies/services/checkVatTestService" \"urn:ec.europa.eu:taxud:vies:services:checkVat:types\"