Aller au contenu principal

Factur‑X et e‑invoicing 2026 avec Symfony : générer des factures conformes via notre bundle open‑source

Corentin Boutillier
9 min de lecture
24 vues

L’e‑invoicing arrive en 2026 en France. Pour anticiper sans sur‑investir, la voie pragmatique consiste à émettre des Factur‑X: un PDF/A‑3 lisible par l’humain avec un XML conforme EN16931 pour la machine. Dans cet article, nous vous montrons comment produire des factures Factur‑X et couvrir les exigences françaises 2026 directement dans Symfony grâce à notre bundle open‑source invoice‑bundle: mapping de votre modèle métier, choix de profil, génération PDF/A‑3, validations métier et intégration.

Comprendre Factur‑X et la réforme e‑invoicing 2026

Factur‑X est un format hybride:

  • un PDF/A‑3 (archivable, pérenne),
  • un XML embarqué (CII/EN16931) exploitable par les plateformes (PPF/PDP) et vos ERP.

Avantages concrets:

  • lisible par vos clients dès aujourd’hui,
  • compatible avec la norme européenne EN16931,
  • évolutif vers la transmission via PPF/PDP (le XML étant la source de vérité).

À partir de 2026, la facturation électronique devient progressivement obligatoire en France (réception pour tous, émission selon la taille d’entreprise). Les plateformes de dématérialisation accepteront des syntaxes standard (CII/EN16931, UBL, et Factur‑X en tant que PDF + XML CII). Vérifiez le calendrier à jour auprès des sources officielles AIFE et des spécifications FNFE‑MPE.

L’objectif: générer aujourd’hui des Factur‑X impeccables et valider tôt vos règles métier pour être prêt le jour J, sans refonte de dernière minute.

Architecture: où se place invoice‑bundle dans Symfony

Notre invoice‑bundle s’insère entre votre domaine et la sortie facture:

  1. Vos objets métier (Commande, Client, Lignes…)
  2. Mapping vers un modèle EN16931 (DTO normés)
  3. Génération du XML (CII) + validations XSD/Schematron
  4. Production d’un PDF/A‑3 et embarquement du XML (attachement AF/Alternative)
  5. Émission/stockage (fichier, S3, email, dépôt futur sur PDP/PPF)

Le bundle expose:

  • un configurateur de mapping (attributs PHP, YAML),
  • un générateur Factur‑X (profils: MINIMUM, BASIC WL, BASIC, EN16931, EXTENDED),
  • un validateur (XSD, règles EN16931, règles France 2026 additionnelles),
  • des helpers PDF/A‑3 (métadonnées, polices, incorp. du XML).

Installation et mise en route

  1. Installer le bundle:
composer require acme/invoice-bundle
  1. L’enregistrer (Symfony Flex le fait généralement pour vous):
## config/bundles.php
return [
    // ...
    Acme\InvoiceBundle\AcmeInvoiceBundle::class => ['all' => true],
];
  1. Configurer le profil et quelques valeurs par défaut:
## config/packages/invoice.yaml
acme_invoice:
  facturx:
    profile: 'EN16931'
    supplier:
      name: 'ACME SAS'
      vat_number: 'FR12345678901'
      siren: '123456789'
      address:
        street: '10 rue de la Facture'
        zip: '75000'
        city: 'Paris'
        country_code: 'FR'

Mapper votre modèle métier vers EN16931

Le mapping traduit vos objets vers les champs normés (BT‑xxx). Exemple avec des attributs sur un DTO “InvoiceView” dédié à l’émission:

final class InvoiceView
{
    public function __construct(
        public string $number,
        public \DateTimeInterface $issueDate,
        public \DateTimeInterface $dueDate,
        public Party $buyer,
        /** @var Line[] */
        public array $lines,
        public string $currency = 'EUR',
        public ?string $purchaseOrderRef = null,
        public ?Payment $payment = null,
        public bool $isCreditNote = false,
    ) {}
}

final class Party { public function __construct(
    public string $name, public ?string $vat=null, public ?string $siren=null,
    public Address $address, public ?string $email=null) {} }

final class Line { public function __construct(
    public string $sku, public string $label, public float $qty,
    public float $unitPrice, public float $vatRate, public ?float $discountPct=null) {} }

final class Payment { public function __construct(
    public string $meansCode='31', public ?string $iban=null, public ?string $bic=null) {} }

Configuration de mapping en YAML (autre option):

## config/invoice/mapping.yaml
acme_invoice:
  mapping:
    document_type_code: "expr: invoice.isCreditNote ? '381' : '380'"
    bt-1-invoice-number: "invoice.number"
    bt-2-issue-date: "invoice.issueDate"
    bt-9-due-date: "invoice.dueDate"
    bt-5-buyer-name: "invoice.buyer.name"
    bt-31-buyer-vat: "invoice.buyer.vat"
    bt-44-buyer-postcode: "invoice.buyer.address.zip"
    bt-47-buyer-country: "invoice.buyer.address.countryCode"
    bt-25-currency: "invoice.currency"
    bt-14-order-reference: "invoice.purchaseOrderRef"
    # Lignes
    lines:
      - bt-126-item-name: "line.label"
        bt-129-item-sku: "line.sku"
        bt-129-1-quantity: "line.qty"
        bt-146-price-amount: "line.unitPrice"
        bt-151-vat-rate: "line.vatRate"
        bt-95-discount-percent: "line.discountPct"

Conseils:

  • Gardez un DTO d’émission indépendant de vos entités. Vous maîtriserez mieux la stabilité du mapping.
  • Stockez les codes ISO (devise, pays), les identifiants (SIREN/SIRET, TVA) et les références (commande, livraison).
  • Normalisez les taux de TVA en décimales (ex: 0.2) et décidez une stratégie d’arrondi (par ligne vs global) cohérente.

Générer un PDF/A‑3 avec XML embarqué

Un service unique orchestre génération XML + PDF:

use Acme\InvoiceBundle\FacturX\Generator;

final class InvoiceController
{
    public function __construct(private Generator $generator) {}

    public function download(string $orderNumber)
    {
        $invoiceView = $this->createInvoiceViewFromOrder($orderNumber);

        $pdfBinary = $this->generator->generate(
            view: $invoiceView,
            template: 'invoice/pdf.html.twig',       // votre template Twig
            filename: sprintf('%s.pdf', $invoiceView->number),
            options: [
                'profile' => 'EN16931',              // ou BASIC, EXTENDED
                'pdfa' => 'PDF/A-3B',
                'embed_xml_name' => $invoiceView->number.'.xml',
            ]
        );

        return new \Symfony\Component\HttpFoundation\Response(
            $pdfBinary,
            200,
            [
                'Content-Type' => 'application/pdf',
                'Content-Disposition' => 'inline; filename="'.$invoiceView->number.'.pdf"',
            ]
        );
    }
}

Le bundle:

  • produit le XML CII/EN16931,
  • vérifie les schémas,
  • incorpore le XML dans le PDF/A‑3 comme pièce jointe “Alternative”,
  • renseigne les métadonnées XMP (titre, auteur, dates) et la langue.

Choisir le bon profil Factur‑X

  • MINIMUM/BASIC WL: suffisant pour de très simples factures, mais limité.
  • EN16931: recommandé en France 2026; couvre la majorité des cas B2B.
  • EXTENDED: pour des besoins complexes (sur‑spécifications sectorielles).

Notre recommandation: viser EN16931 par défaut.

Valider les exigences françaises 2026

Outre la conformité EN16931, des règles nationales s’appliquent. Le bundle propose un validateur “règles FR 2026” que vous pouvez activer en CI:

php bin/console invoice:validate var/invoices/F2026-0001.pdf --rules=fr-2026

Exemples de contrôles:

  • Présence et format des identifiants: SIREN/SIRET, numéro de TVA intracommunautaire.
  • Pays en ISO 3166‑1 alpha‑2, devise ISO 4217.
  • DocumentTypeCode: 380 (facture), 381 (avoir).
  • PaymentMeansCode: 31 (virement SEPA), 49 (prélèvement), etc.
  • Ventilation TVA correcte: base, taux, montant, codes d’exonération le cas échéant.
  • Dates clés: émission, échéance, livraison.
  • Références: commande/contrat si exigées par vos métiers/clients.

Astuce: ajoutez ces validations à vos tests automatisés pour chaque cas métier (escompte, multi‑TVA, avoir partiel, auto‑liquidation, export hors UE).

Couvrir les cas métier fréquents

  • Avoirs et annulations
    • Utilisez DocumentTypeCode 381.
    • Montants des lignes en positif et total négatif, ou lignes négatives: restez cohérent; le validateur tolère les deux si la somme est correcte.
  • Remises et frais
    • Préférez les “allowances/charges” au niveau ligne et/ou document, avec le pourcentage et/ou le montant fixé.
  • Multi‑TVA
    • Séparez les lignes par taux (20 %, 10 %, 5,5 %). Évitez de “moyenner” un taux sur un même article.
  • Exonérations / autoliquidation
    • Renseignez le code de catégorie de taxe et la mention légale (ex: art. 259, 262). Marquez la ligne comme “zero rated” avec le motif.
  • Paiements
    • Fournissez IBAN/BIC pour le virement (code 31), un mandat pour le prélèvement, et les conditions d’escompte.
  • Règles d’arrondi
    • Figez une politique: arrondi au centime, 2 décimales, aligné avec votre ERP. Testez les accumulations (1000 lignes).

Bonnes pratiques PDF/A‑3 et performance

  • PDF/A‑3B suffira pour la plupart des cas; intégrez des polices embarquées (subset) pour garantir l’archivabilité.
  • Évitez les images lourdes; privilégiez le vectoriel pour logos/ICÔNES.
  • Mettez en cache vos templates Twig et pré‑rendez les fichiers statiques (CGV, pied de page).
  • Sur forte volumétrie, déléguez la génération à une file (Messenger) et stockez le PDF + XML sur S3; le bundle expose une commande “generate:invoice” compatible worker.

Intégration Symfony: contrôles, logs et traçabilité

  • Ajoutez une étape “pré‑émission” qui exécute le validateur; si KO, bloquez la facture et remontez des messages exploitables.
  • Logguez l’empreinte (SHA‑256) du PDF et du XML, la version de profil, et l’heure d’émission.
  • Versionnez le mapping: toute évolution doit être testée et auditable.

Exemple de test unitaire:

public function test_facturx_is_valid(): void
{
    $pdf = $this->generator->generate($this->fixtureInvoice(), 'invoice/pdf.html.twig');
    $result = $this->validator->validate($pdf, rules: ['en16931', 'fr-2026']);
    $this->assertTrue($result->passes(), implode("\n", $result->errors()));
}

Anticiper la transmission PPF/PDP

Même si vous démarrez par l’envoi du PDF au client, préparez:

  • la production séparée du XML (export “XML seul”),
  • la gestion des statuts de cycle de vie (déposé, rejeté, accepté, payé) en base,
  • la remontée des événements (webhooks/queues) pour synchroniser vos systèmes.

Le bundle inclut:

  • un extracteur du XML embarqué,
  • un transport HTTP générique pour future intégration PPF/PDP,
  • des schémas d’événements pour tracer les statuts.

Checklist de conformité

  • Profil: EN16931 par défaut
  • Identifiants: SIREN/SIRET, TVA, adresses, pays ISO
  • Dates: émission, échéance, livraison
  • TVA: ventilation par taux, exonérations documentées
  • Paiement: code moyen, IBAN/BIC, conditions
  • Références: commande, BL, contrat si requis
  • PDF/A‑3: polices embarquées, métadonnées XMP
  • Validation: XSD + règles EN16931 + règles FR 2026 en CI
  • Traçabilité: empreintes, logs, version de mapping
  • Volumétrie: génération asynchrone et stockage externalisé si nécessaire

Exemples supplémentaires

Avoir partiel sur une ligne (10 % de remise rétroactive) et auto‑liquidation:

document_type_code: '381'
lines:
  - sku: 'SVC-42'
    label: 'Prestation X (avoir 10%)'
    qty: 1
    unit_price: 1200.00
    vat_rate: 0.0
    tax_category_code: 'AE'        # Reverse charge / autoliquidation
    allowance:
      percent: 10

Sélection du profil BASIC WL pour un cas simplifié:

acme_invoice:
  facturx:
    profile: 'BASIC WL'

Extraction du XML depuis un PDF reçu:

$xml = $this->facturx->extractXml($pdfBinary);
$parsed = $this->facturx->parse($xml); // DTO normalisé

Conclusion

Anticiper la réforme e‑invoicing 2026 ne signifie pas tout reconstruire: en générant dès maintenant des Factur‑X propres (PDF/A‑3 + XML EN16931), vous sécurisez vos flux, vos clients et votre conformité. Notre invoice‑bundle open‑source pour Symfony vous apporte le mapping, les profils, la génération et les validations prêtes à l’emploi.

Passez à l’action:

  • Consultez la documentation et installez le bundle: invoice‑bundle docs
  • Besoin d’un audit de conformité ou d’un accompagnement d’intégration PDP/PPF? Contactez‑nous

Mieux vaut valider aujourd’hui que corriger dans l’urgence demain.

Partager cet article

Logo Vulcain Développement - Développeur Symfony expert vulcain.agency

Développeur Full-Stack freelance expert
Créateur d'applications web sur mesure

📧 vulcain.developpement@gmail.com
📍 Saint-Lô, France

🏗️ Développement Symfony

🔗 API Platform

🏢 Solutions Métier

Liens Rapides