Contexte
- Module :
l10n_fr_einvoicing 18.0, constaté sur le commit 6f61127. Le code concerné est
inchangé sur ff06ad0 (HEAD de la branche 18.0 au 20/09/2026), où le défaut reste donc présent.
- Option société : Auto Reverse Invoice if Refused/Rejected (
fr_ctc_auto_reverse) activée
- Plateforme : rejet technique répété sur un même destinataire (non-conformité PDF/A-3 du Factur-X)
Symptôme
Une facture client rejetée par la plateforme est extournée automatiquement. L'extourne est
transmise à son tour, rejetée pour le même motif, puis extournée le lendemain. La chaîne
progresse d'un document par jour, sans fin.
Constaté en production : 10 documents comptables créés en 5 jours sur un seul client, répartis
sur deux chaînes parallèles.
F68828 (facture d'origine)
-> AV/2026/0198 extourne, rejetée
-> F68883 extourne de l'avoir, rejetée
-> AV/2026/0200 rejetée
-> F68890 rejetée
-> AV/2026/0202 ...
Effet de bord comptable : la chaîne alterne facture et avoir en partant d'une facture et en
terminant par un avoir. Le net est donc un avoir, qui annule la créance d'origine. Le client
ne doit plus rien alors que la facture initiale est toujours due.
Mécanisme
_in_cron() importe un CDAR de statut rejected ou refused et appelle _auto_reverse_invoice(),
qui crée une extourne postée. Le document produit est repris par _out_cron() et retransmis à la
plateforme, qui le rejette pour le même motif que l'original. Le cycle recommence au passage suivant.
La garde existante est inopérante dans ce cas :
# fr_einvoicing_flow.py:924
if move.reversal_move_ids:
msg = f"No auto-reversal because invoice ... has already been reversed"
return False
Elle empêche d'extourner deux fois le même document, mais chaque extourne est un document neuf
qui n'a jamais été extourné. La condition n'est donc jamais vraie le long de la chaîne.
Le marquage fr_einvoicing_internal = True posé par _auto_reverse_invoice() n'empêche pas la
retransmission : il ajoute BT-21 = BAR / ARCHIVEONLY au document, mais _out_cron() génère et
envoie le flux comme pour n'importe quelle autre pièce.
Pistes de correction
-
Ne pas extourner une extourne automatique. Tester move.fr_einvoicing_internal, ou remonter
la chaîne via reversed_entry_id, avant d'appeler _auto_reverse_invoice().
-
Ne pas transmettre les documents fr_einvoicing_internal. Un document marqué ARCHIVEONLY
n'a pas vocation à repartir vers la plateforme ; l'exclure de _out_cron() casse la boucle même
si le point 1 est contourné.
-
Distinguer rejected de refused. Les deux statuts déclenchent aujourd'hui le même
traitement, alors qu'ils sont de nature opposée :
refused est un refus métier du destinataire, l'avoir est justifié ;
rejected est un rejet technique, le document n'a jamais été remis au destinataire. La
réponse attendue est de corriger puis de renvoyer la même facture, pas d'émettre un avoir.
Un rejet de format ne devrait pas produire de pièce comptable.
À défaut d'un correctif, un garde-fou sur le nombre d'extournes automatiques consécutives par
facture d'origine éviterait au moins l'emballement.
Contexte
l10n_fr_einvoicing18.0, constaté sur le commit6f61127. Le code concerné estinchangé sur
ff06ad0(HEAD de la branche 18.0 au 20/09/2026), où le défaut reste donc présent.fr_ctc_auto_reverse) activéeSymptôme
Une facture client rejetée par la plateforme est extournée automatiquement. L'extourne est
transmise à son tour, rejetée pour le même motif, puis extournée le lendemain. La chaîne
progresse d'un document par jour, sans fin.
Constaté en production : 10 documents comptables créés en 5 jours sur un seul client, répartis
sur deux chaînes parallèles.
Effet de bord comptable : la chaîne alterne facture et avoir en partant d'une facture et en
terminant par un avoir. Le net est donc un avoir, qui annule la créance d'origine. Le client
ne doit plus rien alors que la facture initiale est toujours due.
Mécanisme
_in_cron()importe un CDAR de statutrejectedourefusedet appelle_auto_reverse_invoice(),qui crée une extourne postée. Le document produit est repris par
_out_cron()et retransmis à laplateforme, qui le rejette pour le même motif que l'original. Le cycle recommence au passage suivant.
La garde existante est inopérante dans ce cas :
Elle empêche d'extourner deux fois le même document, mais chaque extourne est un document neuf
qui n'a jamais été extourné. La condition n'est donc jamais vraie le long de la chaîne.
Le marquage
fr_einvoicing_internal = Trueposé par_auto_reverse_invoice()n'empêche pas laretransmission : il ajoute
BT-21 = BAR/ARCHIVEONLYau document, mais_out_cron()génère etenvoie le flux comme pour n'importe quelle autre pièce.
Pistes de correction
Ne pas extourner une extourne automatique. Tester
move.fr_einvoicing_internal, ou remonterla chaîne via
reversed_entry_id, avant d'appeler_auto_reverse_invoice().Ne pas transmettre les documents
fr_einvoicing_internal. Un document marquéARCHIVEONLYn'a pas vocation à repartir vers la plateforme ; l'exclure de
_out_cron()casse la boucle mêmesi le point 1 est contourné.
Distinguer
rejectedderefused. Les deux statuts déclenchent aujourd'hui le mêmetraitement, alors qu'ils sont de nature opposée :
refusedest un refus métier du destinataire, l'avoir est justifié ;rejectedest un rejet technique, le document n'a jamais été remis au destinataire. Laréponse attendue est de corriger puis de renvoyer la même facture, pas d'émettre un avoir.
Un rejet de format ne devrait pas produire de pièce comptable.
À défaut d'un correctif, un garde-fou sur le nombre d'extournes automatiques consécutives par
facture d'origine éviterait au moins l'emballement.