Aller au contenu
gaetancottrez.dev

Le cycle de vie d'une facture électronique, côté code : ces statuts qui ne vous appartiennent pas

Publié: at 06:00 | (6 min de lecture)

Table des matières

Ouvrir table des matières

Une facture n’est plus un fichier, c’est une conversation

Dans le monde d’avant, une facture était un document : vous l’émettiez, vous l’archiviez, fin de l’histoire. Dans le monde de la réforme, une facture est une conversation : après l’émission, des messages reviennent — elle a été mise à disposition, elle a été refusée, elle a été payée — et certains de ces messages, c’est vous (enfin, votre plateforme) qui devez les émettre.

C’est le morceau de la réforme que les développeurs sous-estiment le plus. Tout le monde budgète l’émission au bon format (épisode 2) et le raccordement à la plateforme (épisode 3) ; presque personne ne budgète la machinerie des statuts de cycle de vie. Or c’est elle qui transforme votre intégration en vrai système distribué — avec des états, des acteurs multiples et des messages qui se croisent.

Le sujet est suffisamment normé pour avoir sa place dans l’intitulé même de la norme AFNOR du socle : « Formats et Profils des messages Factures et Statuts de cycle de vie » (XP Z12-012). Voyons ce que ça implique dans votre code.

Qui déclare quoi : le renversement à intégrer

Le point mental le plus important : la plupart des statuts d’une facture que vous avez émise sont déclarés par d’autres que vous. La plateforme du destinataire signale la mise à disposition. L’acheteur approuve, refuse, déclare l’encaissement. Ces événements arrivent chez vous — vous ne les décidez pas, vous les apprenez.

Le dispositif distingue par ailleurs des statuts de nature différente — certains sont obligatoires (ils doivent être émis, et transmis à l’administration dans le cadre du dispositif), d’autres sont des jalons facultatifs de suivi plus fin. Le détail précis de la liste et des délais relève des spécifications externes et de la norme XP Z12-012 — je ne vais pas les recopier ici (et pour la norme, je n’en aurais pas le droit) — mais la structure à retenir pour votre conception est celle-ci :

Une facture, quatre déclarants

Modéliser ça proprement : les leçons du terrain

J’ai déjà écrit un article de fond sur la modélisation d’une machine à états dont les statuts viennent d’une spec externe — il est né exactement de ce contexte. Les trois leçons qui comptent le plus, appliquées à la facture :

1. Votre enum interne n’est pas la liste réglementaire. Vous aurez besoin d’états internes que la spec ne connaît pas (« en cours de conversion », « en attente d’accusé de la plateforme »…), et la spec définit des statuts que votre produit n’affichera peut-être jamais tels quels. Séparez les deux vocabulaires et écrivez la table de correspondance — explicitement, dans le code, avec la provenance de chaque statut.

2. Toute transition entrante est une donnée non fiable jusqu’à validation. Un statut arrive de l’extérieur — dans le désordre, en doublon, parfois pour une facture que vous ne connaissez pas (souvenez-vous de la résolution de tenant). Votre machine à états doit valider chaque transition : un statut inattendu ne doit ni planter le flux, ni être appliqué aveuglément, ni être perdu. Registre des non-corrélés, journal des transitions illégales : c’est votre filet.

3. Les délais sont des obligations. Certaines transmissions du dispositif sont assorties de délais. Et un principe de conception en découle, dont j’ai fait un article entier : dans votre code, une échéance réglementaire prime sur toute optimisation interne. Datez chaque événement à sa source, calculez les échéances dès l’entrée, et rendez-les visibles — un délai qu’on découvre en le dépassant est un bug de conception.

Côté implémentation, ça donne le squelette suivant — familier si vous avez lu la série « systèmes contraints » de ce blog :

// Une transition de cycle de vie = un événement entrant à VALIDER, jamais à subir.
async function applyLifecycleStatus(
  event: IncomingStatusEvent
): Promise<Outcome> {
  const invoice = await invoices.findByNumber(
    event.invoiceNumber,
    event.tenantId
  );
  if (!invoice) return record.uncorrelated(event); // conservé, rejouable — jamais détruit

  if (!isLegalTransition(invoice.stage, event.status)) {
    return record.illegalTransition(invoice, event); // journalisé, alerté — jamais appliqué
  }

  // Compare-and-set : le gagnant applique, les doublons échouent sans dégât.
  return invoices.transition(invoice.id, {
    from: invoice.stage,
    to: event.status,
    at: event.occurredAt,
  });
}

Rien d’exotique : de l’idempotence, des transitions gardées, un registre pour l’inclassable. Mais appliqué systématiquement, c’est ce qui sépare une intégration qui « marche en démo » d’une intégration qui survit à deux plateformes qui se répondent dans le désordre.

Ce que votre produit doit montrer (l’angle UX que les devs oublient)

Dernier point, souvent découvert trop tard : ces statuts doivent remonter jusqu’à l’utilisateur. Votre écran « facture » d’avant la réforme affichait payée/impayée. Votre écran d’après doit répondre à des questions nouvelles : est-elle partie ? Est-elle arrivée chez le client ? L’a-t-il refusée, et pourquoi ? Où en est la déclaration côté administration ?

Mon conseil : distinguez visuellement la vie commerciale de la facture (ce que l’acheteur en fait) et sa vie fiscale (ce que le dispositif en atteste). Ce sont deux fils parallèles, pas une seule timeline — les fusionner produit des écrans qui mentent par simplification. Et prévoyez l’affichage du motif pour chaque état d’échec : « refusée » sans le pourquoi génère un ticket de support ; « refusée : numéro de commande absent » génère une correction.

Épilogue

La facture électronique est un objet à états partagés entre quatre acteurs — votre logiciel, deux plateformes et l’acheteur — et votre code n’est propriétaire que d’une fraction de ces états. Modélisez la provenance, validez chaque transition entrante, conservez l’inclassable, traitez les délais comme des obligations, et montrez tout ça à vos utilisateurs sans mentir. C’est un beau problème d’ingénierie, bien plus intéressant que « générer un PDF ».

Pour approfondir chaque brique : la machine à états réglementaire (la modélisation en profondeur), les délais comme obligations, et le prochain épisode : le e-reporting. Besoin d’un renfort qui a déjà construit cette machinerie ? gaetancottrez.dev/facturation-electronique 🔗.

Racontez-moi vos machines à états en commentaire — surtout les transitions que vous n’aviez pas vues venir. Je me ferai une joie de vous répondre.


Sources (consultées en août 2026) : Spécifications externes et normes 🔗 (impots.gouv.fr — norme XP Z12-012 « Formats et Profils des messages Factures et Statuts de cycle de vie ») · Guide pratique de démarrage 🔗 (PDF, DGFiP — questions 11-12 sur rejets et refus). Note de relecture : la liste précise des statuts obligatoires et leurs délais sont à vérifier dans les spécifications externes v3.2 avant publication — cet article reste volontairement au niveau structurel.

Vous pourriez aussi aimer

Intégrer une plateforme agréée : par où commencer quand on est développeur ?

Intégrer une plateforme agréée : par où commencer quand on est développeur ?

Factur-X, UBL, CII : le guide du développeur qui doit produire (ou lire) des factures électroniques

Factur-X, UBL, CII : le guide du développeur qui doit produire (ou lire) des factures électroniques