Comment valider la conformité d'un Factur-X EN 16931
Trois couches de contrôle pour savoir si un PDF Factur-X tiendra le choc.
Dire qu'un fichier est « valide EN 16931 » n'a de sens que si l'on précise contre quoi on a vérifié. La norme européenne EN 16931, le profil Factur-X et la Core Invoice Usage Specification française (CIUS FR) definissent chacun un sous-ensemble de règles, et tous trois doivent passer pour qu'un PDF/A-3 hybride soit considère conforme au calendrier réglementaire français 2026.
Cet article decrit le pipeline de validation que nous utilisons dans le validateur : trois couches successives, des codes d'erreur stables, et un rapport en français. L'objectif est de pouvoir lire le verdict sans connaitre par coeur les annexes techniques du CEN.
1. Que veut dire « valide EN 16931 »
EN 16931 est la norme européenne de facturation électronique publiee par le CEN en 2017 et revisee depuis. Elle definit un modèle semantique (les ~140 termes BT-* et BG-* qui composent une facture) et un ensemble de règles metier identifiees par des codes BR-* et BR-CO-*. Une syntaxe (CII XML chez Factur-X, UBL chez Peppol) est dite conforme si elle expose ce modèle semantique et si l'instance vérifié l'ensemble des règles.
Pour la France, la CIUS FR publiee par la FNFE-MPE ajoute des contraintes propres au marche français : SIRET obligatoire si le vendeur est en FR, schemeID 0002 sur les identifiants legaux, etc. Et la specification Factur-X v1.0.08 précise les règles applicables au calendrier du 1er septembre 2026.
Un PDF Factur-X est donc valide quand il satisfait, dans l'ordre :
- la grammaire XML CII D16B (XSD UN/CEFACT) ;
- les règles metier de la norme EN 16931 (Schematron officiel CEN) ;
- les règles CIUS FR appliquees au-dessus (Schematron FNFE-MPE) ;
- quelques règles supplementaires de coherence (totaux, IBAN, SIRET) qui depassent le cadre Schematron.
2. Notre pipeline en trois couches
Le validateur execute trois couches dans cet ordre. Chaque couche peut produire deserreurs (bloquantes), des avertissements (non bloquants) ou rien. La sequence s'arrete si une couche préalable echoue completement.
| Couche | Outil | Ce qu'elle vérifié | Severite |
|---|---|---|---|
| 1. XSD CII | lxml + schema UN/CEFACT D16B | Structure XML, ordre des éléments, types primitifs, cardinalites | Erreur bloquante |
| 2. Schematron EN 16931 + CIUS FR | Schematron officiel CEN (compile XSLT) + overlay FNFE-MPE | Règles metier BR-*, BR-CO-*, FR-CO-* | Erreur ou avertissement selon le code |
| 3. Règles metier Python | Module apps/api/src/validate/rules.py | SIRET (Luhn), IBAN (MOD 97), totaux croises, devise | Erreur ou avertissement |
Couche 1 : XSD UN/CEFACT CII
Le XML extrait du PDF est valide contre les schemas CrossIndustryInvoice_100pD16B.xsd et ses imports. Cette etape détecté les erreurs structurelles : balise manquante, type incorrect, ordre xsd:sequence non respecte. Si elle echoue, les couches 2 et 3 ne sont pas executees — c'est inutile, le document n'est même pas un CII bien forme.
Couche 2 : Schematron EN 16931 + CIUS FR
On execute le Schematron officiel CEN compile en XSLT, puis l'overlay CIUS FR de la FNFE-MPE. Cette couche produit l'essentiel des codes BR-* que vous verrez dans le rapport. Elle est tolerante : un avertissement (flag="warning") n'empeche pas le verdict global d'être vert si toutes les règles bloquantes passent.
Couche 3 : règles metier Python
Certaines vérifications depassent les capacités de XPath/Schematron. On les implemente en Python dans rules.py :
- Validation SIRET : longueur 14, clé Luhn sur les 14 chiffres. Détecté les SIRET tapes au clavier de travers, fréquents en saisie manuelle.
- Validation IBAN : longueur par pays, contrôle MOD 97 sur la représentation numerique. Un IBAN faux passe le XSD mais sera rejete au virement.
- Coherence des totaux :
BT-109 (HT) + BT-110 (TVA) == BT-112 (TTC)a 2 decimales près. C'est la règleBR-CO-15, mais on la re-vérifié en Python pour rapporter l'ecart numerique exact. - Devise : presence et coherence entre
BT-5 (DocumentCurrencyCode)et la devise des montants ligne.
3. Lire un rapport : vert, ambre, rouge
Le rapport JSON-puis-affiche utilisé trois niveaux de verdict global, eux-mêmes dérivés du cumul des couches :
| Verdict | Signification | Action |
|---|---|---|
| Vert | 0 erreur, 0 avertissement | Diffusable en l'etat au sens EN 16931 + CIUS FR. |
| Ambre | 0 erreur, 1+ avertissement | Le fichier est conforme mais comporte des champs improbables ou redondants. A relire avant émission de masse. |
| Rouge | 1+ erreur bloquante | Non conforme. Corriger les règles listees avant de retransmettre. |
Chaque entree du rapport contient le code de règle (par exemple BR-CO-15), la localisation XPath, le champ semantique BT-* impacte et un message en français. Cette stabilite des codes est volontaire : vous pouvez batir un workflow de correction automatique en routant sur le code.
4. Erreurs fréquentes : BR-CO-15, BR-CO-16, BR-CO-17
Trois règles arrivent en tête des rapports rouges, toutes trois sur la coherence des totaux. Elles meritent d'être comprises car elles signalent presque toujours un problème d'arrondi ou de conversion de devise du logiciel émetteur.
BR-CO-15 : total TTC = total HT + total TVA
BT-112 = BT-109 + BT-110. L'erreur la plus fréquente vient d'un arrondi a 3 decimales en interne puis tronque a 2 decimales en sortie : on obtient un ecart de 0.01 EUR qui suffit a faire echouer la règle. Notre générateur arrondit chaque composant a 2 decimales avant somme, ce qui évite le problème par construction.
BR-CO-16 : montant a payer = TTC - prepaye + arrondi
BT-115 = BT-112 - BT-113 + BT-114. Souvent déclenchée quand un acompte (BT-113) est encode positivement au lieu de venir en deduction. Vérifier le signe dans le mapping du logiciel comptable.
BR-CO-17 : la base TVA par taux = somme des bases ligne du même taux
BT-116 (base TVA catégorie) = somme des BT-131 ayant le même code catégorie. Cette règle bloque des que la decomposition par taux n'est pas coherente avec les lignes. Cas type : une ligne en taux normal 20% mal ventilee dans la catégorie 5.5%, ou un escompte global non reflete sur les bases.
5. Comparer avec B2Brouter pour confirmer
Aucun validateur ne remplace une revue croisee. Le validateur de B2Brouter est gratuit, ne demande pas de compte pour un fichier ponctuel et execute le même Schematron CEN. Si un PDF passe vert chez nous et vert chez B2Brouter, vous avez deux implementations indépendantes du même Schematron qui confirment le verdict.
En cas de divergence, deux explications dans 95% des cas :
- Version de Schematron. B2Brouter peut tarder a intégrer la dernière revision CEN. Notre validateur cible la revision Q4 2024 referencee par Factur-X v1.0.08.
- Overlay CIUS FR. B2Brouter execute par defaut la CIUS belge ou internationale. Pour comparer a egal, selectionner explicitement « Factur-X (FR) » dans leur interface.
Si la divergence persiste après ces deux vérifications, c'est probablement un bug d'implementation — le notre ou le leur. Dans ce cas, le code de règle expose dans le rapport permet de chercher la définition normative dans l'annexe Schematron du CEN pour trancher.
Ce qu'il faut retenir
- « Valide EN 16931 » n'a de sens que cumule avec CIUS FR pour la France.
- Trois couches dans l'ordre : XSD CII → Schematron CEN + FR → règles metier Python.
- Le verdict vert/ambre/rouge agrege les trois couches ; le code de règle reste stable et utilisable en aval.
- Les erreurs
BR-CO-15/16/17sont presque toujours des arrondis ou des signes mal ventilles : a corriger en amont dans le logiciel émetteur. - Confirmer avec un second outil (B2Brouter) avant de declarer un cas litigieux comme tel.
Pour aller plus loin sur les profils susceptibles d'influencer les règles applicables, voir les différences entre profils MINIMUM, BASIC, EN 16931 et EXTENDED et les 18 champs obligatoires du profil MINIMUM. Pour explorer le contenu d'un PDF avant validation, l'extracteur renvoie le XML brut, et le visualiseur donne une vue lisible du couple PDF + donnees.