Technique

Valider EN16931 / Factur-X : XSD vs Schematron, debug erreurs BR-* (guide technique)

11 min de lecture Par FacturX API

XSD valide la forme, Schematron valide le fond. Guide technique pour comprendre et corriger les erreurs BR-* dans vos factures EN16931 / Factur-X.

Un fichier Factur-X peut être structurellement parfait — chaque balise présente, chaque namespace déclaré, chaque type correct — et être rejeté par une PDP. La raison : la validation XSD ne couvre qu’une fraction des exigences EN16931. Le reste est vérifié par les règles Schematron, et c’est là que la majorité des erreurs surviennent en production.

Cet article détaille les deux niveaux de validation, explique la logique derrière les codes d’erreur BR-*, et propose un workflow de débogage reproductible pour localiser et corriger chaque type d’échec.

Pour le contexte général de la réforme et le pipeline complet de validation, voir Facturation électronique 2026 : le guide technique complet pour développeurs.

Deux niveaux, deux logiques

La validation EN16931 n’est pas un processus unique. C’est une chaîne de deux passes indépendantes, chacune avec sa propre logique et ses propres artefacts.

XSD : la validation structurelle

Le XML Schema Definition (XSD) vérifie que le document XML est bien formé selon le modèle de données attendu. Concrètement, le XSD contrôle :

  • La présence des éléments obligatoires
  • La cardinalité (un élément attendu une seule fois n’apparaît pas deux fois)
  • Les types de données (un montant est un decimal, une date est au format YYYY-MM-DD)
  • Les namespaces (les préfixes rsm:, ram:, qdt: pointent vers les bonnes URN)
  • L’ordre des éléments enfants dans les séquences

Ce que le XSD ne peut pas vérifier :

  • Que le total TVA correspond à la somme des lignes
  • Que le code devise est un code ISO 4217 réellement existant
  • Que les champs conditionnellement obligatoires sont présents selon le contexte
  • Que les montants sont arithmétiquement cohérents

Un fichier XSD-valide garantit uniquement que le parser peut le lire sans erreur. Il ne garantit pas que la facture est sémantiquement correcte.

Les XSD à utiliser dépendent du format :

FormatXSD source
CII (Factur-X)CrossIndustryInvoice_100pD22B.xsd
UBL 2.1UBL-Invoice-2.1.xsd

Namespaces racines correspondants :

  • CII : urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100
  • UBL : urn:oasis:names:specification:ubl:schema:xsd:Invoice-2

Pour Factur-X, les XSD sont distribués dans le pack de conformité FNFE-MPE, avec des schémas et Schematron par profil. Factur-X 1.08 / ZUGFeRD 2.4 utilisent CII D22B, une extension de D16B (liaison officielle CEN) rétrocompatible. Pour UBL, les schémas sont disponibles via OASIS.

Schematron : la validation sémantique

Schematron est un langage de validation basé sur des assertions XPath. Contrairement au XSD qui décrit la structure attendue, le Schematron exprime des règles métier sous forme de conditions logiques.

Chaque règle est identifiée par un code unique préfixé BR- (Business Rule). Elle est composée de :

  • Un contexte : le nœud XML sur lequel la règle s’applique (expression XPath)
  • Un test : une assertion XPath qui doit être vraie
  • Un message : l’explication de l’échec si l’assertion est fausse

Exemple simplifié d’une règle Schematron EN16931 :

<rule context="/rsm:CrossIndustryInvoice/rsm:ExchangedDocument">
  <assert test="ram:ID[string-length(.) > 0]"
    id="BR-02"
    flag="fatal">
    [BR-02] Une facture doit comporter un numéro de facture (BT-1).
  </assert>
</rule>

Cette règle vérifie que l’élément ram:ID dans ExchangedDocument existe et n’est pas vide. Si le champ est absent ou vide, le validateur remonte l’erreur BR-02.

Les fichiers Schematron EN16931 sont publiés par le CEN TC 434. Les extensions nationales (règles BR-FR-* pour la France, BR-DE-* pour l’Allemagne) sont maintenues séparément par les organismes nationaux.

Anatomie d’un code BR-*

Les identifiants de règles suivent une convention de nommage structurée :

PréfixeSignificationExemples
BR-Business Rule — règle cœur EN16931BR-02, BR-05, BR-16
BR-CO-Coherence — cohérence arithmétique entre totauxBR-CO-10, BR-CO-14, BR-CO-15
BR-CL-Code List — validation des codes (devise, pays, unités)BR-CL-04, BR-CL-17
BR-S-Catégorie TVA “Standard rated” (code S)BR-S-01, BR-S-05
BR-Z-Catégorie TVA “Zero rated” (code Z)BR-Z-01, BR-Z-05
BR-E-Catégorie TVA “Exempt” (code E)BR-E-01, BR-E-05
BR-AE-Catégorie TVA “Reverse charge” (code AE)BR-AE-01
BR-IC-Catégorie TVA “Intra-community” (code IC)BR-IC-01
BR-G-Catégorie TVA “Export hors UE” (code G)BR-G-01
BR-O-Catégorie TVA “Out of scope” (code O)BR-O-01
BR-DE-CIUS allemande (XRechnung) — extension nationaleBR-DE-1, BR-DE-21
BR-FR-Extensions françaises — selon spécifications FNFE-MPEBR-FR-CTC-*

Chaque famille BR-{code}- correspond à une catégorie TVA du code UNCL 5305. Les règles de chaque famille vérifient la cohérence entre le code catégorie, le taux déclaré et les montants associés.

La distinction est importante : les règles BR- et BR-CO- sont universelles EN16931 — elles s’appliquent à toute facture conforme, quel que soit le pays. Les règles BR-DE-*, BR-FR-* sont des extensions nationales (CIUS) et ne s’appliquent que dans le contexte du pays concerné.

Confondre les deux est une erreur fréquente. Un validateur configuré pour XRechnung (Allemagne) appliquera les règles BR-DE-* qui n’ont aucun sens pour une facture française. Inversement, un validateur EN16931 générique n’appliquera pas les règles BR-FR-* spécifiques à la France. Le choix du jeu de règles Schematron doit correspondre au contexte d’émission.

Les erreurs les plus fréquentes

L’analyse des retours de validation sur les factures Factur-X et UBL révèle un ensemble récurrent d’erreurs. Les voici, regroupées par catégorie.

Erreurs de cohérence arithmétique (BR-CO-*)

BR-CO-14 — Total TVA ≠ somme des catégories

La règle impose que le total TVA de la facture (BT-110) soit égal à la somme des montants de TVA par catégorie fiscale (BT-117). Un écart, même de 0,01 €, déclenche l’erreur.

Cause principale : arrondi calculé globalement au lieu d’être agrégé ligne par ligne. Si chaque ligne arrondit son montant TVA à 2 décimales, la somme de ces arrondis peut différer du total arrondi indépendamment.

<!-- Erreur : 3 lignes à 33,33 € HT, TVA 20% -->
<!-- Ligne 1 : 33.33 × 0.20 = 6.666 → arrondi 6.67 -->
<!-- Ligne 2 : 33.33 × 0.20 = 6.666 → arrondi 6.67 -->
<!-- Ligne 3 : 33.33 × 0.20 = 6.666 → arrondi 6.67 -->
<!-- Somme lignes : 6.67 + 6.67 + 6.67 = 20.01 -->
<!-- Total global : 99.99 × 0.20 = 19.998 → arrondi 20.00 -->
<!-- Écart : 0.01 → BR-CO-14 déclenché -->

Correction : toujours calculer le total TVA comme la somme des montants TVA par catégorie déjà arrondis, pas comme un calcul indépendant sur le total HT.

BR-CO-15 — Montant TTC incohérent

Le montant dû TTC (BT-112) doit être égal au montant HT (BT-109) plus le total TVA (BT-110). Même logique d’arrondi que BR-CO-14, mais sur l’étage supérieur de l’agrégation.

BR-CO-10 — Total HT ≠ somme des lignes

Le total HT de la facture (BT-106) doit correspondre à la somme des montants nets des lignes (BT-131). Souvent déclenché quand des remises ou majorations au niveau document (BG-20, BG-21) ne sont pas intégrées dans le calcul.

Erreurs de champs obligatoires (BR-*)

BR-02 — Numéro de facture absent

Le champ BT-1 (numéro de facture) est obligatoire et ne peut pas être vide. Certains ERP génèrent un XML avant l’attribution définitive du numéro, produisant un champ vide.

BR-16 — Aucune ligne de facture

La facture doit contenir au moins une ligne (BG-25). Un XML de facture sans nœud IncludedSupplyChainTradeLineItem (CII) ou cac:InvoiceLine (UBL) déclenche cette erreur. Cela arrive quand un ERP génère un brouillon vide ou un avoir sans ligne de détail.

BR-05 — Code devise absent

Le code devise de la facture (BT-5) est obligatoire. L’erreur survient quand le champ InvoiceCurrencyCode est absent ou vide. La validité du code lui-même (ISO 4217 alpha-3) est vérifiée par une règle distincte, BR-CL-04.

Erreurs de codes et listes (BR-CL-*)

BR-CL-04 — Code devise invalide

Même si le champ BT-5 est présent (BR-05 OK), la valeur doit être un code ISO 4217 alpha-3 valide. Un code comme EURO au lieu de EUR, ou une chaîne vide, déclenche cette erreur.

BR-CL-17 — Code de catégorie TVA invalide

Le code de catégorie TVA (BT-151) doit être un code UNCL 5305 valide : S (standard), Z (taux zéro), E (exonéré), AE (autoliquidation), K (intra-communautaire), G (export hors UE), O (hors scope TVA), L (IGIC Canaries) ou M (IPSI Ceuta/Melilla). Toute autre valeur est rejetée.

Erreurs spécifiques TVA (BR-S-*)

BR-S-05 — Taux TVA invalide pour catégorie “Standard rated”

Quand le code catégorie TVA est S (standard rated), le taux de TVA (BT-152) doit être renseigné et supérieur à zéro. Un taux absent ou à 0% avec un code S déclenche l’erreur — il faut utiliser le code Z pour un taux à zéro.

Workflow de débogage

Face à une erreur Schematron, le réflexe est souvent de chercher “BR-CO-14 fix” sur Google. C’est rarement productif. Voici un workflow systématique plus efficace.

Étape 1 — Lire le rapport de validation

Un rapport de validation structuré contient trois informations essentielles :

{
  "id": "BR-CO-14",
  "message": "Le total TVA de la facture doit correspondre à la somme des montants de TVA par catégorie fiscale.",
  "location": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation"
}
  • id : identifie la règle. Le préfixe indique la catégorie (CO = cohérence, CL = code list, S = TVA).
  • message : décrit ce que la règle attend. Lire le message, pas juste le code.
  • location : le chemin XPath du nœud où l’erreur a été détectée. C’est le point de départ du debug.

Étape 2 — Localiser dans le XML

Ouvrir le fichier XML et naviguer jusqu’au nœud XPath indiqué. Pour Factur-X (CII), la structure suit un arbre prévisible :

CrossIndustryInvoice
├── ExchangedDocumentContext     → profil, identifiant
├── ExchangedDocument            → numéro, date, type
└── SupplyChainTradeTransaction
    ├── IncludedSupplyChainTradeLineItem[]    → lignes
    ├── ApplicableHeaderTradeAgreement        → vendeur, acheteur
    ├── ApplicableHeaderTradeDelivery         → livraison
    └── ApplicableHeaderTradeSettlement       → paiement, TVA, totaux
        ├── ApplicableTradeTax[]              → détail TVA par catégorie
        └── SpecifiedTradeSettlementHeaderMonetarySummation  → totaux

Les erreurs arithmétiques (BR-CO-) pointent généralement vers SpecifiedTradeSettlementHeaderMonetarySummation ou ApplicableTradeTax. Les erreurs de champs (BR-) pointent vers le nœud parent du champ manquant.

Étape 3 — Identifier la cause racine

Pour chaque catégorie d’erreur, la cause racine typique est différente :

CatégorieCause racine habituelle
BR-CO-*Logique d’arrondi dans le moteur de calcul ERP
BR-* (champ manquant)Mapping incomplet entre les données ERP et le modèle XML
BR-CL-*Référentiel de codes non aligné avec EN16931 (devise, pays, unités)
BR-S-*Incohérence entre le code catégorie TVA et le taux déclaré

Étape 4 — Corriger à la source

La correction doit se faire dans le système qui génère le XML, pas dans le XML directement. Modifier un XML à la main peut résoudre le symptôme, mais l’erreur réapparaîtra à la prochaine génération.

Pour les erreurs d’arrondi :

  • Identifier où dans le code le total est calculé (agrégation des lignes vs calcul global)
  • Appliquer la règle : arrondir chaque ligne, puis sommer les arrondis
  • Ne jamais calculer le total TVA indépendamment de la somme par catégorie

Pour les champs manquants :

  • Vérifier le mapping ERP → XML pour le champ concerné
  • Identifier si le champ est présent dans l’ERP mais non exporté, ou absent de l’ERP
  • Ajouter le mapping ou la valeur par défaut appropriée

Pour les codes invalides :

  • Vérifier la liste de codes utilisée par votre système contre la liste officielle EN16931
  • Les listes de codes EN16931 sont publiées par le CEF (Connecting Europe Facility) sous forme de genericode

Étape 5 — Re-valider

Après correction, re-valider le fichier complet (XSD + Schematron). Une correction peut en révéler une autre — par exemple, corriger un arrondi TVA sur une catégorie peut décaler le total TTC et déclencher BR-CO-15.

# Validation complète via l'API
curl -s -X POST https://facturxapi.com/api/v1/validate \
  -H "X-API-Key: votre-cle-api" \
  -F "[email protected]" | jq '.errors'

Si le résultat est un tableau vide [], la facture est conforme.

Pièges courants

Piège 1 — Valider uniquement en XSD

Beaucoup de validateurs “basiques” (intégrés aux libs de génération) n’exécutent que la validation XSD. Un fichier qui passe leur contrôle peut contenir 15 erreurs Schematron. La validation XSD seule donne un faux sentiment de sécurité.

Piège 2 — Mauvais jeu de Schematron

Le même fichier CII peut être validé contre :

  • Les Schematron EN16931 cœur (CEN TC 434)
  • Les Schematron XRechnung (Allemagne)
  • Les Schematron Factur-X profil (FNFE-MPE)

Utiliser le mauvais jeu peut générer des faux positifs (erreurs allemandes sur une facture française) ou des faux négatifs (règles françaises non vérifiées). Le jeu de Schematron doit correspondre au profil déclaré dans le GuidelineSpecifiedDocumentContextParameter/ID du XML.

Piège 3 — Version obsolète des artefacts

Les fichiers Schematron et les listes de codes sont mis à jour régulièrement. La version 1.3.13 d’EN16931 n’a pas les mêmes règles que la 1.3.11. Des factures valides hier peuvent devenir invalides avec une nouvelle version de Schematron — et inversement. Un validateur embarqué qui n’est pas mis à jour régulièrement valide selon des règles potentiellement obsolètes.

Piège 4 — Confondre warning et error

Certains validateurs distinguent les erreurs fatales (flag="fatal") des avertissements (flag="warning"). Un warning ne bloque pas la conformité EN16931, mais peut indiquer un problème qui sera rejeté par une PDP spécifique ou une extension nationale. Les PDP peuvent avoir des exigences supplémentaires au-delà d’EN16931.

Automatiser dans le pipeline

La validation manuelle est viable pour quelques factures par mois. Au-delà, elle doit être automatisée. Deux approches :

Validation dans les tests unitaires

import httpx
import pytest

API_URL = "https://facturxapi.com/api/v1/validate"

def validate(pdf_path: str, api_key: str) -> dict:
    with open(pdf_path, "rb") as f:
        resp = httpx.post(
            API_URL,
            headers={"X-API-Key": api_key},
            files={"file": f},
            timeout=30,
        )
    resp.raise_for_status()
    return resp.json()

def test_facture_standard_valide(api_key):
    result = validate("tests/fixtures/facture-standard.pdf", api_key)
    assert result["valid"] is True, f"Erreurs: {result['errors']}"

def test_facture_avoir_valide(api_key):
    result = validate("tests/fixtures/avoir-2026-001.pdf", api_key)
    assert result["valid"] is True, f"Erreurs: {result['errors']}"

Validation en CI/CD (pre-deploy)

# .github/workflows/validate-invoices.yml
name: Validate invoice templates
on: [push]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate all test invoices
        run: |
          for pdf in tests/fixtures/*.pdf; do
            result=$(curl -s -X POST "$FACTURX_API_URL/validate" \
              -H "X-API-Key: $FACTURX_API_KEY" \
              -F "file=@$pdf")
            valid=$(echo "$result" | jq -r '.valid')
            if [ "$valid" != "true" ]; then
              echo "FAIL: $pdf"
              echo "$result" | jq '.errors'
              exit 1
            fi
            echo "PASS: $pdf"
          done
        env:
          FACTURX_API_URL: https://facturxapi.com/api/v1
          FACTURX_API_KEY: ${{ secrets.FACTURX_API_KEY }}

Cette approche garantit que toute modification du code de génération de factures est vérifiée avant déploiement. Un changement dans le moteur de calcul qui introduit un écart d’arrondi sera détecté automatiquement.

Ressources

La clé API gratuite permet de valider vos fichiers immédiatement. Le rapport JSON structuré fournit les codes BR-*, les messages explicatifs et les chemins XPath — exactement ce dont le workflow ci-dessus a besoin pour diagnostiquer rapidement chaque erreur.

#en16931 #schematron #xsd #validation #BR-* #factur-x #debug