Sur cette page
Répondez de tête, puis ouvrez. Faire sortir les chiffres d'un document
1L'outil rend un tableau complet, au bon format. Les chiffres sont-ils justes ?
Pas forcément. Les éditeurs garantissent la forme, pas les valeurs : un chiffre mal lu ou inventé a la même forme qu'un juste. Seules des règles, ou une personne, le repèrent.
2Un fournisseur vous envoie désormais des factures électroniques au format UBL. Faut-il les extraire ?
Non : elles arrivent déjà en données, transmises par une plateforme agréée. L'extraction reste utile pour les documents qui arrivent encore en PDF ou sur papier.
- Une facture qui porte ses données, comme une Factur-X, se lit sans modèle : le programme ouvre le fichier joint et recopie les champs.
- Le modèle ne sert qu'aux PDF sans données et aux scans ; on lui demande de recopier, de ne rien calculer, et de laisser vide ce qu'il ne lit pas.
- Les règles vérifient l'identité du fournisseur, avec la clé du SIREN et le numéro de TVA, puis les montants ; une facture qui échoue va à une personne.
- Une erreur de recopie qui respecte les règles, un numéro de facture par exemple, ne se voit qu'en comparant à un corrigé.
- Depuis le 1er septembre 2026, toute entreprise doit pouvoir recevoir des factures électroniques ; celles qui arrivent en données n'ont pas besoin d'être lues par un modèle.
Objectif
Mettre trois factures fournisseurs dans un même format de dix champs, prêt pour l'import comptable : le fournisseur, son SIREN, son numéro de TVA, le numéro de facture, les dates d'émission et d'échéance, le total hors taxe, le taux de TVA, la TVA et le total TTC. Une facture n'entre dans le fichier d'import que si toutes les règles passent ; les autres vont à une personne, avec la raison.
Les trois factures sont fabriquées : entreprises fictives, numéros et montants inventés. Les SIREN commencent par 00 et ont une clé valide ; ne les cherchez pas dans un annuaire. L'appel au modèle (étape 3) suit la documentation d'Anthropic et n'a pas encore été exécuté par Meridal ; ses lectures, utilisées aux étapes 4 et 5, sont fabriquées. Toutes les autres étapes ont tourné : leurs sorties sont réelles.
Données fournies
Trois factures reçues en septembre 2026 par une menuiserie fictive. Elles disent les mêmes choses, mais ne se lisent pas de la même façon.
Facture A
Document fabriqué pour l'exercice de Meridal : entreprise fictive, numéros et montants inventés. Reçue en Factur-X : un PDF qui porte en pièce jointe le fichier factur-x.xml.
Atelier Rivière (entreprise fictive)
SIREN 004128757 · TVA FR62004128757FACTURE n° F-2026-0918
Date : 18/09/2026
Échéance : 18/10/2026
Client : Menuiserie Valcourt (entreprise fictive), SIREN 007391642Données fournies Désignation Montant HT Maintenance des machines, septembre 1 200,00 € Total hors taxe : 1 200,00 €
TVA à 20 % : 240,00 €
Total TTC : 1 440,00 €Facture B
Document fabriqué pour l'exercice de Meridal : entreprise fictive, numéros et montants inventés. Reçue en PDF ordinaire : la page contient du texte, sans données jointes.
Imprimerie du Port (entreprise fictive)
SIREN 002583169 · TVA FR92002583169FACTURE n° 8834
Date : 22/09/2026
Échéance : 22/10/2026
Client : Menuiserie Valcourt (entreprise fictive), SIREN 007391642Données fournies Désignation Montant HT Impression de 500 catalogues 1 850,00 € Livraison 150,00 € Total hors taxe : 2 000,00 €
TVA à 20 % : 400,00 €
Total TTC : 2 400,00 €Facture C
Document fabriqué pour l'exercice de Meridal : entreprise fictive, numéros et montants inventés. Reçue scannée : la page n'est qu'une image, un peu de travers ; une tache couvre la date d'échéance.
Transports Lemaire (entreprise fictive)
SIREN 006513477 · TVA FR84006513477FACTURE n° T-77
Date : 25/09/2026
Échéance : (illisible : une tache couvre la date)
Client : Menuiserie Valcourt (entreprise fictive), SIREN 007391642Données fournies Désignation Montant HT Livraison régionale 800,00 € Total hors taxe : 800,00 €
TVA à 20 % : 160,00 €
Total TTC : 960,00 €
Les PDF se fabriquent avec fabriquer.py, que l'étape 1 suppose lancé : a.pdf porte son fichier factur-x.xml en pièce jointe, b.pdf a une couche de texte, c.pdf n'est qu'une image. Ce ne sont pas des Factur-X conformes à la norme, qui demande un PDF/A-3 et un profil validé : seulement de quoi exercer la lecture.
Voir le programme qui fabrique les trois PDF
fabriquer.py"""Fabrique les trois factures de l'exercice dans pdf/, et l'image du scan dans c.png : a.pdf porte son fichier factur-x.xml en pièce jointe,
b.pdf est un PDF ordinaire avec sa couche de texte, c.pdf ne contient qu'une image, comme un scan.
Ce ne sont pas des Factur-X conformes (PDF/A-3, profil validé) : seulement de quoi exercer la lecture."""
import io, os
from decimal import Decimal
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from pypdf import PdfReader, PdfWriter
from donnees import FACTURES, CLIENT
os.makedirs("pdf", exist_ok=True)
FR = lambda v: f"{Decimal(v):,.2f}".replace(",", " ").replace(".", ",") + " €"
JOUR = lambda d: f"{d[8:]}/{d[5:7]}/{d[:4]}"
def lignes_texte(f):
t = [f"{f['fournisseur']} (entreprise fictive)", f"SIREN {f['siren']} · TVA {f['tva_intracom']}", "",
f"FACTURE n° {f['numero']}", f"Date : {JOUR(f['date_emission'])}",
f"Échéance : {JOUR(f['date_echeance']) if f['date_echeance'] else ' '}",
f"Client : {CLIENT[0]}, SIREN {CLIENT[1]}", ""]
t += [f"{lib} : {FR(v)} HT" for lib, v in f["lignes"]]
t += ["", f"Total hors taxe : {FR(f['total_ht'])}", f"TVA à {f['taux_tva']} % : {FR(f['total_tva'])}", f"Total TTC : {FR(f['total_ttc'])}"]
return t
def pdf_texte(f):
buf = io.BytesIO(); c = canvas.Canvas(buf, pagesize=A4); y = 780
for l in lignes_texte(f):
c.setFont("Helvetica-Bold" if l.startswith("FACTURE") else "Helvetica", 11); c.drawString(60, y, l); y -= 20
c.save(); return buf.getvalue()
def xml_cii(f):
"""Les champs de la facture en Cross Industry Invoice, les chemins que lit la bibliothèque factur-x."""
return f'''<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocument>
<ram:ID>{f["numero"]}</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime><udt:DateTimeString format="102">{f["date_emission"].replace("-", "")}</udt:DateTimeString></ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>{f["fournisseur"]}</ram:Name>
<ram:SpecifiedLegalOrganization><ram:ID schemeID="0002">{f["siren"]}</ram:ID></ram:SpecifiedLegalOrganization>
<ram:SpecifiedTaxRegistration><ram:ID schemeID="VA">{f["tva_intracom"]}</ram:ID></ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:ApplicableTradeTax><ram:CalculatedAmount>{f["total_tva"]}</ram:CalculatedAmount><ram:TypeCode>VAT</ram:TypeCode><ram:BasisAmount>{f["total_ht"]}</ram:BasisAmount><ram:CategoryCode>S</ram:CategoryCode><ram:RateApplicablePercent>{f["taux_tva"]}</ram:RateApplicablePercent></ram:ApplicableTradeTax>
<ram:SpecifiedTradePaymentTerms><ram:DueDateDateTime><udt:DateTimeString format="102">{f["date_echeance"].replace("-", "")}</udt:DateTimeString></ram:DueDateDateTime></ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:TaxBasisTotalAmount>{f["total_ht"]}</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">{f["total_tva"]}</ram:TaxTotalAmount>
<ram:GrandTotalAmount>{f["total_ttc"]}</ram:GrandTotalAmount>
<ram:DuePayableAmount>{f["total_ttc"]}</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
'''
def pdf_scan(f):
im = Image.new("L", (1240, 1754), 248); d = ImageDraw.Draw(im)
police = ImageFont.truetype("/System/Library/Fonts/Supplemental/Arial.ttf", 30); y = 140
for l in lignes_texte(f):
d.text((120, y), l, fill=40, font=police); y += 52
d.ellipse((560, 395, 720, 455), fill=150) # une tache sur la date d'échéance
im = im.rotate(-1.2, fillcolor=248).filter(ImageFilter.GaussianBlur(0.8))
im.save("c.png") # la page en image, que lit extraire.py
buf = io.BytesIO(); c = canvas.Canvas(buf, pagesize=A4)
tmp = io.BytesIO(); im.save(tmp, "PNG"); tmp.seek(0)
from reportlab.lib.utils import ImageReader
c.drawImage(ImageReader(tmp), 0, 0, *A4); c.save(); return buf.getvalue()
w = PdfWriter(clone_from=PdfReader(io.BytesIO(pdf_texte(FACTURES["a"]))))
w.add_attachment("factur-x.xml", xml_cii(FACTURES["a"]).encode())
w.write("pdf/a.pdf")
open("pdf/b.pdf", "wb").write(pdf_texte(FACTURES["b"]))
open("pdf/c.pdf", "wb").write(pdf_scan(FACTURES["c"]))
print("fabriquées :", ", ".join(sorted(os.listdir("pdf"))))Consignes
Étape 1. Reconnaître la forme
Avant toute lecture, le programme regarde ce que contient le fichier : des données jointes, du texte, ou une image seule. La forme décide de la lecture, et du coût.
trier.pyfrom pypdf import PdfReader
def forme(chemin):
"""Données jointes, texte, ou image seule : ce qui décide de la façon de lire."""
lecteur = PdfReader(chemin)
if "factur-x.xml" in lecteur.attachments:
return "Factur-X : lire le XML joint, sans modèle"
texte = "".join(page.extract_text() or "" for page in lecteur.pages)
if len(texte.strip()) > 50:
return f"PDF avec texte ({len(texte)} caractères) : le modèle lit le texte"
return "image seule : le modèle lit l'image, ou un OCR avant lui"
for nom in ("a.pdf", "b.pdf", "c.pdf"):
print(nom, "→", forme(f"pdf/{nom}"))python3 trier.pya.pdf → Factur-X : lire le XML joint, sans modèleb.pdf → PDF avec texte (344 caractères) : le modèle lit le textec.pdf → image seule : le modèle lit l'image, ou un OCR avant lui
Essayé le 9 octobre 2026 avec Python 3.12.7, pypdf 6.17.0 et reportlab 4.3.1, sur les factures fabriquées de la page.
Étape 2. Lire la Factur-X sans modèle
Factur-X est un standard franco-allemand : un PDF lisible par une personne, qui porte les données de la facture dans un fichier XML pour les machines. C'est l'un des trois formats que toute entreprise et toute plateforme agréée doivent accepter en réception. Les chemins des champs sont ceux qu'utilise la bibliothèque libre factur-x d'Akretion.
lire_facturx.pyimport json
import xml.etree.ElementTree as ET
from pypdf import PdfReader
NS = {"rsm": "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100",
"ram": "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100",
"udt": "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"}
VENDEUR = "rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/"
REGLEMENT = "rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/"
TOTAUX = REGLEMENT + "ram:SpecifiedTradeSettlementHeaderMonetarySummation/"
CHAMPS = {
"fournisseur": VENDEUR + "ram:Name",
"siren": VENDEUR + "ram:SpecifiedLegalOrganization/ram:ID",
"tva_intracom": VENDEUR + "ram:SpecifiedTaxRegistration/ram:ID[@schemeID='VA']",
"numero": "rsm:ExchangedDocument/ram:ID",
"date_emission": "rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString",
"date_echeance": REGLEMENT + "ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString",
"total_ht": TOTAUX + "ram:TaxBasisTotalAmount",
"taux_tva": REGLEMENT + "ram:ApplicableTradeTax/ram:RateApplicablePercent",
"total_tva": TOTAUX + "ram:TaxTotalAmount",
"total_ttc": TOTAUX + "ram:GrandTotalAmount",
}
def date_iso(s): # format 102 : AAAAMMJJ
return f"{s[:4]}-{s[4:6]}-{s[6:]}" if s else None
xml = PdfReader("pdf/a.pdf").attachments["factur-x.xml"][0]
racine = ET.fromstring(xml)
facture = {nom: racine.findtext(chemin, namespaces=NS) for nom, chemin in CHAMPS.items()}
facture["date_emission"] = date_iso(facture["date_emission"])
facture["date_echeance"] = date_iso(facture["date_echeance"])
print(json.dumps(facture, ensure_ascii=False, indent=1))
json.dump(facture, open("lecture_a.json", "w"), ensure_ascii=False)python3 lire_facturx.py{ "fournisseur": "Atelier Rivière", "siren": "004128757", "tva_intracom": "FR62004128757", "numero": "F-2026-0918", "date_emission": "2026-09-18", "date_echeance": "2026-10-18", "total_ht": "1200.00", "taux_tva": "20", "total_tva": "240.00", "total_ttc": "1440.00"}
Essayé le 9 octobre 2026 avec Python 3.12.7, pypdf 6.17.0 et reportlab 4.3.1, sur les factures fabriquées de la page.
Aucun modèle, aucune erreur de lecture possible : les montants sont ceux que le fournisseur a envoyés. Les dates arrivent au format 102, AAAAMMJJ, et se convertissent.
Étape 3. Faire lire le reste par un modèle
Pour b.pdf, le modèle lit le texte de la page ; pour c.pdf, l'image. Chaque champ peut rester vide : sur des documents denses ou des formulaires, la faute typique des modèles est d'inventer une valeur pour un champ vide. La consigne le dit, et le format le permet. Le format imposé garantit la forme, pas la justesse des valeurs. Sur une image floue, tournée ou petite, Anthropic prévient que Claude peut se tromper. Les mêmes appels chez OpenAI, Google et Mistral : réponse dans un format imposé.
extraire.pyimport base64
from typing import Optional
import anthropic
from pydantic import BaseModel, Field
class Facture(BaseModel):
fournisseur: Optional[str]
siren: Optional[str] = Field(description="9 chiffres, sans espace")
tva_intracom: Optional[str] = Field(description="numéro de TVA du fournisseur, sans espace")
numero: Optional[str]
date_emission: Optional[str] = Field(description="AAAA-MM-JJ")
date_echeance: Optional[str] = Field(description="AAAA-MM-JJ")
total_ht: Optional[str] = Field(description="tel qu'écrit, point décimal, sans symbole")
taux_tva: Optional[str]
total_tva: Optional[str]
total_ttc: Optional[str]
CONSIGNE = ("Recopie les champs de cette facture. Ne calcule rien. "
"Si un champ est absent ou illisible, laisse-le vide (null) : n'en devine aucun.")
client = anthropic.Anthropic()
def lire(contenu):
reponse = client.messages.parse(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": contenu + [{"type": "text", "text": CONSIGNE}]}],
output_format=Facture,
)
return reponse.parsed_output
# b.pdf : le texte de la page, tiré par pypdf
from pypdf import PdfReader
texte = PdfReader("pdf/b.pdf").pages[0].extract_text()
print(lire([{"type": "text", "text": texte}]))
# c.pdf : la page en image (un PNG tiré du scan)
png = base64.standard_b64encode(open("c.png", "rb").read()).decode()
print(lire([{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": png}}]))python3 extraire.pyPas encore exécuté par Meridal. Les lectures fabriquées utilisées à l'étape 4 sont dans lectures_modele.json : celle de b.pdf porte un numéro de TVA aux deux derniers chiffres inversés ; celle de c.pdf laisse l'échéance vide.
Étape 4. Contrôler, puis écrire le fichier d'import
Six règles, de l'identité aux montants. Le SIREN a neuf chiffres, dont le dernier est une clé de contrôle, calculée par la formule de Luhn ; le numéro de TVA français est FR, une clé de deux chiffres, puis le SIREN ; les taux de TVA de France métropolitaine sont 20 %, 10 %, 5,5 % et 2,1 %. Les montants restent en texte et se convertissent en Decimal, qui ne les arrondit pas.
controler.pyimport csv, json, re
from decimal import Decimal
TAUX = {Decimal("20"), Decimal("10"), Decimal("5.5"), Decimal("2.1")}
def cle_siren(siren):
"""Clé de Luhn : les chiffres de rang pair, lus de gauche à droite, comptent double."""
total = 0
for rang, c in enumerate(siren, start=1):
d = int(c) * (2 if rang % 2 == 0 else 1)
total += d - 9 if d > 9 else d
return total % 10 == 0
def regles(f):
vides = [nom for nom, v in f.items() if v in (None, "")]
if vides:
return [f"champ vide : {', '.join(vides)}"]
ht, taux, tva, ttc = (Decimal(f[k]) for k in ("total_ht", "taux_tva", "total_tva", "total_ttc"))
echecs = []
if not (re.fullmatch(r"\d{9}", f["siren"]) and cle_siren(f["siren"])):
echecs.append("SIREN invalide")
if not re.fullmatch(r"FR\d{2}" + f["siren"], f["tva_intracom"]):
echecs.append("le n° de TVA ne contient pas le SIREN")
if taux not in TAUX:
echecs.append(f"taux {taux} % inconnu en France métropolitaine")
if abs(ht * taux / 100 - tva) > Decimal("0.01"):
echecs.append("TVA ≠ HT × taux")
if ht + tva != ttc:
echecs.append("TTC ≠ HT + TVA")
if f["date_echeance"] < f["date_emission"]:
echecs.append("échéance avant l'émission")
return echecs
lot = {"a": json.load(open("lecture_a.json"))} | {k: v for k, v in json.load(open("lectures_modele.json")).items() if k in "bc"}
retenues = []
for nom, f in lot.items():
echecs = regles(f)
print(f"{nom} {f['fournisseur']:<20} {'à importer' if not echecs else 'à revoir : ' + ' ; '.join(echecs)}")
if not echecs:
retenues.append(f)
with open("import.csv", "w", newline="") as sortie:
w = csv.DictWriter(sortie, fieldnames=list(lot["a"]), delimiter=";")
w.writeheader(); w.writerows(retenues)
print(f"import.csv : {len(retenues)} facture(s)")python3 controler.pya Atelier Rivière à importerb Imprimerie du Port à revoir : le n° de TVA ne contient pas le SIRENc Transports Lemaire à revoir : champ vide : date_echeanceimport.csv : 1 facture(s)
Essayé le 9 octobre 2026 avec Python 3.12.7, pypdf 6.17.0 et reportlab 4.3.1, sur les factures fabriquées de la page.
Étape 5. Mesurer l'extraction
La Factur-X donne un corrigé : sa page lue par le modèle se compare à ses données jointes. Dix champs, comparés un à un.
mesurer.pyimport json
# a.pdf a deux versions : ses données jointes (le corrigé) et la page, lue par le modèle
corrige = json.load(open("lecture_a.json"))
lue = json.load(open("lectures_modele.json"))["a_vue"]
justes = [nom for nom in corrige if lue[nom] == corrige[nom]]
for nom in corrige:
if nom not in justes:
print(f"écart sur {nom} : lu {lue[nom]!r}, attendu {corrige[nom]!r}")
print(f"{len(justes)} champs justes sur {len(corrige)}")python3 mesurer.pyécart sur numero : lu 'F-2026-0916', attendu 'F-2026-0918'9 champs justes sur 10
Essayé le 9 octobre 2026 avec Python 3.12.7, pypdf 6.17.0 et reportlab 4.3.1, sur les factures fabriquées de la page.
Critères de réussite
Mentions obligatoires
Les mentions obligatoires d'une facture comprennent la date d'émission, un numéro unique pris dans une séquence continue, l'identité du vendeur et son numéro de TVA, le décompte de chaque ligne avec son taux, les totaux hors taxe et TTC, la date de règlement. Avec la facturation électronique s'ajoutent le SIREN du client, l'adresse de livraison si elle diffère, la nature des opérations, biens ou services, et l'option pour la TVA sur les débits ; d'abord pour les grandes entreprises et les ETI, puis pour les PME à partir du 1er septembre 2027. Les dix champs de l'exercice n'en sont qu'une partie : ceux dont la comptabilité a besoin pour enregistrer et payer.
Indications
pypdf ne trouve pas de pièce jointe dans une vraie Factur-X
CauseCertains logiciels nomment autrement le fichier joint : les anciennes versions allemandes utilisent zugferd-invoice.xml ou ZUGFeRD-invoice.xml.
À faireChercher aussi ces noms, ou passer par la fonction get_xml_from_pdf de la bibliothèque factur-x, qui les connaît.
Le texte tiré d'un PDF est dans le désordre
CauseUn PDF place des morceaux de texte sur la page ; l'ordre de lecture n'y est pas toujours écrit, surtout en colonnes et en tableaux.
À faireEnvoyer au modèle la page en image plutôt que le texte, ou passer par un outil qui reconstruit la mise en page : lecture de documents.
Le modèle remplit un champ que la facture ne porte pas
CauseLe champ est obligatoire dans le format, ou la consigne ne dit pas quoi faire d'un champ absent.
À faireRendre chaque champ facultatif (Optional) et le dire dans la consigne ; puis traiter un champ vide comme une raison de faire revoir.
Le TTC ne tombe pas juste à un centime près
CauseDes montants lus en nombres à virgule flottante, ou une TVA arrondie ligne à ligne par le fournisseur.
À faireGarder les montants en Decimal ; tolérer un centime sur la TVA, jamais sur l'addition du TTC.
Correction commentée
Ouvrir la correction
La forme d'abordUne seule des trois factures a besoin d'un modèle pour sa page de texte, une autre pour son image ; la Factur-X n'en a besoin d'aucun. Lire les données jointes quand elles existent supprime l'erreur de lecture, et le coût.
La facture bLe modèle a lu FR92002583196 ; le SIREN est 002583169. Les deux derniers chiffres sont inversés. Les montants sont justes : sans la règle d'identité, la facture partait à l'import avec un numéro de TVA faux.
La facture cL'échéance est sous une tache ; le modèle l'a laissée vide, comme demandé. Une personne la lit sur le papier, ou la demande au fournisseur. Un modèle qui aurait mis une date plausible, trente jours après l'émission par exemple, aurait passé toutes les règles.
La mesureLe numéro F-2026-0916 au lieu de F-2026-0918 passe les six règles : rien dans la facture ne permet de le contrôler. Seule la comparaison à un corrigé le trouve. Sur un vrai lot, comptez les champs justes, champ par champ, sur les factures dont vous avez les données.
Extensions
- Un lot entier, avec les doublons et la liste des factures mises de côté : le projet d'extraction de factures.
- Un seuil de confiance : certains services rendent une confiance par champ ; Microsoft propose de s'en servir pour accepter ou faire revoir, et AWS suggère 90 % ou plus pour une décision financière.
- Les scans difficiles : un OCR spécialisé avant le modèle, et ses limites : lecture de documents.
- La réception électronique : depuis le 1er septembre 2026, toute entreprise doit pouvoir recevoir des factures électroniques ; une PME émet les siennes à partir du 1er septembre 2027. Ce que cela change : la facture électronique.
Lectures suivantes
Extraire une facture dans un format imposé
Les pages qui renvoient ici
Limites et sources
Les trois factures, les SIREN, les numéros de TVA et les lectures du modèle sont fabriqués. Le fichier a.pdf porte un XML Factur-X, mais n'est pas un PDF/A-3 et n'a pas été validé contre un profil de la norme.
L'appel au modèle suit la documentation d'Anthropic et n'a pas encore été exécuté par Meridal. Les autres programmes ont tourné le 9 octobre 2026 avec Python 3.12.7, pypdf 6.17.0 et reportlab 4.3.1.
La clé de Luhn du SIREN est décrite par l'IREM de Limoges ; l'Insee dit seulement que le neuvième chiffre est une clé de contrôle. Le contrôle du numéro de TVA vérifie qu'il contient le SIREN, pas sa clé.
- Service-public.fr, mentions obligatoires sur une facture ; numéro de TVA intracommunautaire ; Insee, numéro Siren ; economie.gouv.fr, taux de TVA.
- FNFE-MPE, Factur-X ; Akretion, bibliothèque factur-x ; IREM de Limoges, codes au quotidien.
- Le relevé :
apprendre/recherches/2026-10-07-extraction-documents.json.
Écrit par Meridal, avec l'aide de l'IA ; chaque fait vérifié à sa source et relu avant publication. Texte et schémas sous licence CC BY 4.0 : reprenez-les en citant Meridal. Le code sous licence MIT.
Préparer les documents qui reviennent
1 650 € à 5 100 €, puis sur devis