Aller au contenu principal
fellwin
Finance · Conformité fiscale2026 → en cours · Facturation automatique depuis juillet 2026 · Concepteur · développeur · exploitant

Spécialiste européen du reconditionné

Une facture émise automatiquement, jamais deux fois, jamais sur un compte deviné.

  • 26

    cas comptables nommés, chacun avec son historique de comptes

  • 994

    tests sur le seul périmètre facturation

  • 0

    compte comptable deviné : à défaut de règle, l'export s'arrête

En bref

Le problème
Dans un groupe à deux entités et deux comptabilités nationales, une facture est un acte irréversible : émise deux fois, elle se rattrape par un avoir ; posée sur le mauvais compte, elle fausse une déclaration déjà déposée.
La décision
Trois services qui ne partagent aucune base s'accordent sur une facture unique, et la comptabilité devient une configuration datée plutôt que du code : les règles désignent un cas nommé, le numéro de compte derrière ce cas est éditable par le cabinet comptable, sans déploiement.
Le résultat
Le système refuse plutôt que de deviner : une facture est réservée avant d'être créée, un transfert entre entités attend la preuve de réception, et un document sans règle applicable sort avec un compte vide que l'import comptable rejette.

Le contexte

Notre client vend sur plusieurs canaux à travers deux entités juridiques, dans deux pays, avec deux comptabilités et deux administrations fiscales en face.

Une facture est le seul objet du système qu'on ne peut pas corriger en la supprimant : une fois émise, elle est numérotée, communiquée et déclarée.

Tout le reste de la plateforme se rattrape : un stock faux se recalcule, un prix faux se recalcule, une synchronisation ratée se rejoue. Une facture, non. Elle porte un numéro dans une séquence légale, elle part chez le client, elle entre dans une déclaration. Émise en double, elle se corrige par un avoir et une explication. Posée sur le mauvais compte comptable, elle fausse une déclaration que le cabinet a déjà déposée.

C'est ce qui sépare cette partie du système du reste : ici, une reprise de données n'est pas une option de secours.

Trois services participent à une même facture sans qu'aucun ne la possède : l'un détient la configuration comptable, l'un émet, l'un déclenche.

Le catalogue détient la configuration comptable : quel compte, quelle taxe, quelles conditions de règlement pour quelle combinaison de pays, de segment et de régime. Le service financier émet la facture. Le service opérations déclenche, parce que c'est lui qui sait ce qui est réellement parti.

Aucun des trois ne partage de base de données avec un autre. La configuration traverse la frontière comme un contrat versionné, publié en entier sur le bus d'événements, et le service financier le recopie chez lui. Au moment de facturer, il résout contre sa propre copie et n'interroge jamais le catalogue : un document comptable ne doit pas dépendre de la disponibilité d'un autre service.

Les décisions d'architecture

La réservation d'une facture est écrite avant que la facture soit créée, parce qu'un enregistrement posé après coup ne peut que constater le doublon.

Un registre de réservation : la ligne est écrite avant l'appel de création, puis promue, pas Vérifier après coup qu'aucune facture n'existe déjà pour ces colis. La notification de départ se déclenche une fois par colis, pas une fois par commande : une commande partie en quatre colis a lancé quatre fois la même facturation en trente secondes (mesuré le 26 août 2026). Le compteur « quantité déjà facturée » de l'ERP ne peut pas servir de garde : il est tenu par ligne de commande, donc une ligne expédiée en deux colis affiche un seul nombre pour les deux. Et un enregistrement écrit après la création ne peut que constater le doublon, à un moment où le client en a déjà reçu deux. L'ordre est tout le dessin : réserver, créer, promouvoir. L'unicité est portée par un index unique en base, pas par une lecture préalable qui peut toujours se faire doubler.

Trade-off assumé : Un processus interrompu laisse des réservations à l'état « réservée ». Elles portent l'identifiant du travail qui les a prises, de sorte qu'un ré-essai reprenne les siennes et se heurte à celles d'un autre. Une création réussie suivie d'un échec d'écriture laisse des lignes à promouvoir à la main, journalisées comme telles. Surtout : la réservation n'est relâchée que si rien n'a été facturé. La relâcher après une création réussie reviendrait à offrir les mêmes colis au prochain essai, donc à facturer deux fois pour avoir voulu faire le ménage.

Les règles comptables vivent dans le code, les numéros de compte dans une configuration datée que le cabinet modifie sans déploiement.

Chaque règle désigne un cas comptable nommé ; le numéro derrière ce cas vit dans une configuration datée, éditable sans déploiement, pas Les numéros de compte écrits dans le code, et les colonnes fiscales de l'export DATEV conditionnées sur la valeur du compte. C'est la version précédente du système, et son mode de défaillance est documenté. Un même numéro servait plusieurs règles sans rapport entre elles : déplacer un cas entraînait les autres avec lui. L'export écrivait la clé fiscale allemande en comparant le compte à une valeur littérale ; déplacer ce compte pour un seul cas faisait suivre la clé sur les mauvais documents, en silence, dans un fichier importé tel quel par le cabinet comptable. Enfin, une table plate n'a pas d'axe temporel : ré-exporter un mois déjà déposé après une réorganisation du plan comptable produisait un fichier différent de celui qui avait été déposé. Les 26 cas sont donc nommés et séparés, chacun porte sa propre ligne de temps, et un document se résout toujours à sa propre date.

Trade-off assumé : La configuration devient un prérequis d'exploitation : tant qu'elle n'est pas chargée sur un environnement, l'export refuse de répondre plutôt que de sortir une ligne fausse. Ajouter un cas devient une procédure ordonnée sur chaque environnement, et non un simple déploiement. Et modifier une période passée change ce que produit le ré-export d'une période déjà déposée : c'est assumé, avec l'auteur et l'horodatage de chaque modification comme trace.

Quand aucune règle ne s'applique, le système s'arrête et le dit, au lieu de retomber sur un compte par défaut.

Aucune valeur par défaut nulle part sur le chemin d'un document comptable, pas Un compte de repli, et une recherche « la dernière valeur connue gagne ». Le résolveur de comptes refuse d'être construit si un seul des cas n'a aucune période définie, et il nomme ceux qui manquent. La couverture temporelle est validée à chaque écriture : exactement une période sans date de début, aucun trou, aucun chevauchement, une fin ouverte. C'est ce qui rend « la recherche ne peut jamais retomber sur un défaut » une propriété structurelle et non une convention. Si malgré tout aucune règle ne s'applique à un document, le compte sort vide : l'import DATEV rejette la ligne, le document est corrigé à la source, puis ré-exporté. Un repli, lui, aurait posé de l'argent réel sur un compte que personne n'a choisi, sans que rien ne le signale.

Trade-off assumé : Une configuration oubliée sur un environnement arrête tout l'export de l'entité concernée, documents sans rapport compris. C'est le prix accepté, et il est explicite dans la procédure de déploiement : charger la configuration avant de promouvoir la version qui la lit.

Un transfert entre les deux entités est une vente, pas un mouvement de stock : une ligne par numéro de série, le même montant des deux côtés au centime, et aucune marche arrière.

Le transfert s'écrit comme une vente réelle entre les deux entités : une ligne par numéro de série, un prix calculé côté serveur uniquement, et une correction qui passe par la comptabilité, pas Une ligne par référence avec une quantité, et un bouton d'annulation qui défait les documents. Deux unités de la même référence n'ont pas le même coût d'acquisition, donc pas le même prix de transfert : une ligne portant une quantité ne sait pas l'exprimer. Le prix est calculé côté serveur et nulle part ailleurs, de sorte que le même montant atterrisse sur la commande de vente de l'entité qui cède et sur la commande d'achat de celle qui reçoit, au centime près ; la marge forfaitaire est répartie au plus fort reste pour que la somme des lignes retombe exactement sur le total du lot. Et le lien entre les deux comptabilités ne repose pas sur la parole des opérations : la réception ne transmet que l'identifiant de la commande d'achat, et le service financier relit dans l'ERP les numéros de série que la réception a réellement enregistrés. Ce sont eux qui décident si une facture est émise.

Trade-off assumé : Annuler un transfert terminé n'est pas un chemin de code, c'est une procédure comptable : retour de marchandise, puis avoir, puis note de débit. Le sens inverse du transfert n'est pas une simple bascule, parce qu'un transfert inverse est une écriture invalide. Et la règle de coût est contre-intuitive pour un développeur : un transfert interne étant administratif, il ne doit jamais gonfler un prix de revient, donc toute recherche de coût renvoie le minimum constaté sur l'ensemble des entités, pas celui de l'entité qui interroge.

L'exécution

Les contrôles qui précèdent une facture sont ordonnés, et cet ordre est une décision à part entière.

Les contrôles s'exécutent du moins coûteux au plus coûteux, et celui qui écrit passe en dernier, pour qu'une commande arrêtée plus tôt ne laisse aucune trace.

La séquence va de la configuration pas encore synchronisée aux écotaxes réglementaires, puis aux frais de port incohérents, puis à l'attente d'une réception entre entités. Ce dernier contrôle passe après les autres parce que le vérifier écrit une ligne de journal : une commande arrêtée plus tôt ne doit pas laisser derrière elle la trace d'une expédition qu'on n'a pas facturée.

Le contrôle de la première facture d'un nouveau client professionnel est posé tout à la fin, pour ne dépenser sa lecture de l'ERP que sur une commande ayant franchi tout le reste. Il ne bloque d'ailleurs plus rien : la facture est créée, gardée en brouillon et signalée pour relecture. La version précédente la refusait, ce qui obligeait quelqu'un à la ressaisir.

La distinction qui structure tout cela n'est pas « valide ou invalide » mais « ré-essayable ou définitif ». Un contrôle ré-essayable rend la main à la file de tâches, qui repasse après temporisation. Un contrôle définitif enregistre la raison, commente la commande dans l'ERP et prévient un humain par courriel.

L'étape qui facture un transfert ne peut pas le faire échouer : à ce moment la marchandise a déjà bougé, juridiquement et physiquement.

Quand le transfert arrive à sa facturation, ses cinq documents sont confirmés dans les deux comptabilités et le stock a changé de propriétaire. Un échec à cet instant produit une facture manquante, pas une facture fausse, et la seule marche arrière disponible consisterait à supprimer cinq documents réels pour corriger un papier.

L'étape ne rejette donc rien. Son prix : une facture manquante devient invisible sur un transfert affiché comme terminé. Elle est pour cela enregistrée deux fois, sur le transfert et dans une liste de reprises manuelles, et une vérification repasse toutes les dix minutes relire l'état réel de la facture. La règle tient en quatre mots : mise en file n'est pas facturée.

garde-fousCertains tests ne vérifient pas un comportement : ils lisent les sources et échouent si une règle est réimplémentée ailleurs.

Les préfixes qui identifient une écotaxe ne sont reconnus qu'à un seul endroit du code. Un test parcourt l'ensemble des sources et échoue si un autre fichier les épelle. La raison est écrite à côté : une seconde comparaison de préfixe écrite en ligne compile, passe tous les tests de sa propre fonctionnalité, et fait silencieusement diverger ce qui est facturé comme une taxe de ce qui est exporté comme une vente. Seul un balayage des sources l'attrape.

Même dessin pour l'envoi d'une facture : le test repère les appels qui libèrent un document à ce qu'ils demandent (un envoi, un courriel, un indicateur), pas au nom de la méthode, et échoue sur tout appel placé hors de l'unique point de passage qui vérifie l'état du brouillon. Et un troisième garde le vocabulaire du domaine, parce que le préfixe retenu pour les transferts entre entités devait absolument ne pas se confondre avec celui que l'ERP utilise déjà pour ses transferts internes.

Les leçons

Leçon 1« On ne sait pas encore » ne doit jamais se lire « cette commande est fausse ».

Au démarrage du service, la synchronisation de la configuration part sans être attendue : l'exécuteur se met à vider sa file avant que la copie locale soit remplie. Le résolveur traitait une copie vide comme une erreur définitive. Résultat : chaque travail mis en file pendant un déploiement ressortait marqué « permanent », avec un commentaire posté sur une vraie commande client, pour une copie qui se remplissait quelques secondes plus tard.

Le correctif n'est pas d'attendre le démarrage, c'est de distinguer les deux situations dans le code : « la configuration n'est pas encore là » est ré-essayable, « cette commande ne remplit pas les conditions » est définitif. La même forme s'applique aux taxes réglementaires.

Leçon 2Un automatisme qui ne se déclenche pas ne dit rien, et une attente légitime ne produit aucune alerte.

La réception d'un transfert entre entités n'était apprise que d'une seule manière : une automatisation de l'ERP receveur. Une automatisation qui ne part pas n'émet aucune erreur. Les numéros de série restaient « en attente de réception », le planificateur répondait sobrement « en attente », et aucune alerte n'existait pour cet état, parce que l'attente est parfaitement légitime sur toutes les autres commandes. Des transferts sont restés dans cette situation, non facturés, sans que rien ne le signale.

Le correctif est le même que celui des factures : une seconde voie, explicite et authentifiée, qui permet aux opérations de déclarer la réception au lieu d'espérer qu'elle soit annoncée. Elle appelle exactement la même méthode que l'automatisation, pour qu'il n'y ait qu'un chemin de code et qu'une piste d'audit. L'automatisation d'origine reste active en filet, donc les deux déclencheurs qui se présentent pour un même transfert est le cas attendu : c'est précisément pour cela que le registre de réservation n'est pas optionnel.

Leçon 3Le partage proportionnel des frais entre plusieurs factures doit se fermer au centime, sans quoi la TVA porte sur un total qui en manque un.

Une commande expédiée en plusieurs fois donne plusieurs factures, et les frais portés en tête de commande doivent être répartis entre elles. La répartition évidente, part par part, ne se referme pas : 10,00 € sur trois factures donnaient trois fois 3,33 €, soit 9,99 €, et la TVA était calculée sur 9,99 €.

La part de chaque facture se calcule désormais sur un cumul : on arrondit le total jusqu'à cette facture incluse, on en retranche le total arrondi jusqu'à la précédente. La facture qui porte la dernière marchandise ferme le montant exactement. La base de répartition est la valeur des marchandises seules, écotaxes et services exclus, parce que les compter diluait la part de chaque commande portant une garantie.

Leçon 4Une vérification nocturne demande au service financier de résoudre des scénarios, plutôt que de comparer des lignes de configuration.

Comparer les lignes des deux côtés ne verrait qu'une chose : la copie locale qui prend du retard. Demander au service financier de résoudre des scénarios voit cela, plus deux implémentations de priorité qui divergent, plus deux dérivations de zone géographique qui ne s'accordent pas.

C'est la dernière qui compte, parce qu'il y en a délibérément deux : un côté découpe par continent, l'autre par appartenance à l'Union. Les deux ne se rejoignent pas sur une poignée de pays, et cet écart connu est comparé en premier pour qu'il remonte comme une seule anomalie au lieu de dix conséquences.

En production aujourd'hui

La facturation automatique tourne pour l'entité française, déclenchée par le départ de la marchandise. Le basculement de la seconde entité est préparé et commandé par des drapeaux distincts par entité et par déclencheur : c'est volontaire, parce que la bascule passe par une fenêtre où deux émetteurs coexistent, et qu'une fenêtre où deux émetteurs coexistent est exactement d'où viendrait une facture en double.

Les transferts entre les deux entités tournent en production de bout en bout : les cinq documents écrits dans les deux comptabilités, la réservation des numéros de série pendant que la marchandise bouge, la facturation de la vente interne, et une réconciliation qui repasse toutes les dix minutes vérifier qu'une facture annoncée existe réellement.

L'export DATEV est en service. Le cabinet comptable reçoit le fichier d'écritures et l'importe tel quel : chaque document y porte son compte résolu à sa propre date, de sorte qu'un mois déjà déposé se ré-exporte à l'identique.

Le périmètre facturation porte 994 tests sur 63 fichiers, et le module de transfert entre entités 471 de plus. Une partie d'entre eux ne vérifie pas un résultat mais une discipline : qu'une règle reste écrite à un seul endroit.

Un système à concevoir, déployer ou reprendre en main ?

Avec ou sans cahier des charges : décrivez votre situation, nous répondons par écrit sous 48 h ouvrées.

Réservez un diagnostic