Sur cette page
- Trois temps : lire chaque facture avec un format imposé, la vérifier par des règles, la ranger.
- Le modèle recopie ; le programme calcule et juge. Aucun montant n'est calculé par le modèle.
- Une facture qui échoue n'est ni corrigée ni comblée : elle est mise de côté, avec la règle en échec, pour une personne.
- Les doublons et les dates absentes se voient par des règles aussi simples que les calculs.
- Le fichier final ne contient que ce qui a passé toutes les règles.
Objectif
Écrire un programme qui lit huit factures, garde celles qui passent toutes les règles dans un fichier acceptees.csv, et met les autres de côté en disant pourquoi.
Les factures sont fabriquées ; cinq portent une anomalie voulue. La lecture par le modèle (étape 1) suit la documentation d'OpenAI lue le 7 octobre 2026 et n'a pas encore été exécutée par Meridal ; la vérification et le rangement ont tourné, sur la lecture attendue donnée dans la correction.
Données fournies
- Lot de huit factures fournisseursTélécharger
FACTURE n° F-101 du 2 septembre 2026
Atelier Rivière entreprise fictive
- DésignationMontant HT
- Maintenance des machines1 200,00 €
Total hors taxe1 200,00 €TVA à 20 %240,00 €Total TTC1 440,00 €FACTURE n° 2026-0412 du 15 septembre 2026
Cabinet Exemple entreprise fictive
- DésignationMontant HT
- Conseil, 10 heures1 000,00 €
- Frais de déplacement250,00 €
Total hors taxe1 250,00 €TVA à 20 %250,00 €Total TTC1 500,00 €FACTURE n° 8834 du 9 septembre 2026
Imprimerie du Port entreprise fictive
- DésignationMontant HT
- Impression de catalogues2 000,00 €
Total hors taxe2 000,00 €TVA à 20 %400,00 €Total TTC2 040,00 €FACTURE n° T-77 du 11 septembre 2026
Transports Lemaire entreprise fictive
- DésignationMontant HT
- Livraison régionale800,00 €
Total hors taxe800,00 €TVA à 20 %120,00 €Total TTC920,00 €FACTURE n° F-101 du 2 septembre 2026
Atelier Rivière entreprise fictive
- DésignationMontant HT
- Maintenance des machines1 200,00 €
Total hors taxe1 200,00 €TVA à 20 %240,00 €Total TTC1 440,00 €FACTURE n° 2026-55
Bureau Fournitures Sud entreprise fictive
- DésignationMontant HT
- Papier et classeurs150,00 €
Total hors taxe150,00 €TVA à 20 %30,00 €Total TTC180,00 €FACTURE n° C-3021 du 22 septembre 2026
Café Service Pro entreprise fictive
- DésignationMontant HT
- Location de machine60,00 €
- Capsules45,00 €
Total hors taxe120,00 €TVA à 20 %24,00 €Total TTC144,00 €FACTURE n° L-2026-09 du 30 septembre 2026
Librairie Technique entreprise fictive
- DésignationMontant HT
- Ouvrages professionnels300,00 €
Total hors taxe300,00 €TVA à 5,5 %16,50 €Total TTC316,50 €
Aussi en Word, pour l'essayer dans un assistant : lot-de-factures.docx.
Consignes
Format et règles
Le format de la page précédente, avec une date qui peut être vide ; et cinq règles, dont deux nouvelles : la date présente, et la facture pas déjà reçue (même fournisseur, même numéro).
facture.pyfrom pydantic import BaseModel, Field
class Ligne(BaseModel):
libelle: str
montant_ht: float
class Facture(BaseModel):
fournisseur: str
numero: str
date: str = Field(description="Date au format AAAA-MM-JJ ; chaîne vide si la facture n'en porte pas")
lignes: list[Ligne]
total_ht: float
taux_tva: float = Field(description="Taux de TVA en pour cent, par exemple 20 ou 5.5")
total_tva: float
total_ttc: float
def controler(f, deja_vues, ecart=0.01):
"""Les règles d'une facture recevable ; le modèle lit, le programme vérifie."""
regles = [
("date présente", f.date != ""),
("hors taxe = somme des lignes", abs(sum(l.montant_ht for l in f.lignes) - f.total_ht) <= ecart),
("TVA = hors taxe × taux", abs(f.total_ht * f.taux_tva / 100 - f.total_tva) <= ecart),
("total = hors taxe + TVA", abs(f.total_ht + f.total_tva - f.total_ttc) <= ecart),
("pas déjà reçue", (f.fournisseur, f.numero) not in deja_vues),
]
return [nom for nom, passe in regles if not passe]Étape 1. Lire le lot
Une facture par section du fichier ; chacune lue avec le format imposé, et la lecture gardée dans lectures.json. Pour un autre éditeur, remplacez l'appel par celui de son onglet dans la page du format imposé.
lire_lot.pyimport json
import re
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
from openai import OpenAI
from facture import Facture
client = OpenAI()
texte = Path("lot-de-factures.md").read_text(encoding="utf-8")
factures = re.split(r"\n## Facture \d+\n", texte)[1:] # une facture par section
lectures = []
for t in factures:
r = client.responses.parse(
model="gpt-6-luna",
input=[
{"role": "system", "content": "Extrais les informations de la facture. "
"Recopie les montants tels qu'ils sont écrits ; ne calcule rien."},
{"role": "user", "content": t},
],
text_format=Facture,
)
lectures.append(r.output_parsed.model_dump())
Path("lectures.json").write_text(json.dumps(lectures, ensure_ascii=False, indent=1), encoding="utf-8")
print(len(lectures), "factures lues")uv run lire_lot.pyPas encore exécuté par Meridal. Ce qu'on attend : « 8 factures lues », et un fichier lectures.json de huit lectures.
Étape 2. Vérifier et ranger
Chaque lecture passe les cinq règles ; celles qui passent toutes vont dans acceptees.csv, séparé par des points-virgules pour s'ouvrir dans un tableur français.
verifier_lot.pyimport csv
import json
from facture import Facture, controler
lectures = [Facture(**x) for x in json.load(open("lectures.json", encoding="utf-8"))]
deja_vues, acceptees, de_cote = set(), [], []
for n, f in enumerate(lectures, 1):
echecs = controler(f, deja_vues)
deja_vues.add((f.fournisseur, f.numero))
(de_cote if echecs else acceptees).append((n, f, echecs))
print(f"Facture {n} ({f.numero}) :", "acceptée" if not echecs else "de côté, règle en échec : " + " ; ".join(echecs))
with open("acceptees.csv", "w", newline="", encoding="utf-8") as sortie:
w = csv.writer(sortie, delimiter=";")
w.writerow(["fournisseur", "numero", "date", "total_ht", "total_tva", "total_ttc"])
for _, f, _ in acceptees:
w.writerow([f.fournisseur, f.numero, f.date, f.total_ht, f.total_tva, f.total_ttc])
print(f"{len(acceptees)} acceptées, écrites dans acceptees.csv ; {len(de_cote)} mises de côté")Critères de réussite
Indications
Une facture juste est mise de côté
CauseLe modèle a mal recopié un montant, ou l'a recalculé.
À faireComparez sa lecture au texte ; la consigne « recopie les montants tels qu'ils sont écrits ; ne calcule rien » est là pour ça.
La facture à 5,5 % échoue sur la TVA
CauseLe taux a été lu 5 au lieu de 5,5, ou recopié en texte avec sa virgule.
À faireVérifiez la description du champ taux_tva : un nombre, avec un point décimal.
La facture sans date reçoit une date
CauseLe modèle a mis la date du jour, ou celle d'une autre facture.
À faireLa description du champ dit « chaîne vide si la facture n'en porte pas » ; gardez-la, et testez cette facture à part.
Les montants s'arrondissent mal
CauseUn nombre à virgule flottante ne tombe pas toujours juste.
À faireLes règles tolèrent un centime d'écart ; pour une vraie comptabilité, passer par Decimal.
Correction commentée
Ouvrir la correction
La lecture attendueCe que l'étape 1 doit produire, recopié de chaque facture sans rien calculer :
lectures.json[
{"fournisseur": "Atelier Rivière", "numero": "F-101", "date": "2026-09-02", "lignes": [{"libelle": "Maintenance des machines", "montant_ht": 1200.0}], "total_ht": 1200.0, "taux_tva": 20.0, "total_tva": 240.0, "total_ttc": 1440.0},
{"fournisseur": "Cabinet Exemple", "numero": "2026-0412", "date": "2026-09-15", "lignes": [{"libelle": "Conseil, 10 heures", "montant_ht": 1000.0}, {"libelle": "Frais de déplacement", "montant_ht": 250.0}], "total_ht": 1250.0, "taux_tva": 20.0, "total_tva": 250.0, "total_ttc": 1500.0},
{"fournisseur": "Imprimerie du Port", "numero": "8834", "date": "2026-09-09", "lignes": [{"libelle": "Impression de catalogues", "montant_ht": 2000.0}], "total_ht": 2000.0, "taux_tva": 20.0, "total_tva": 400.0, "total_ttc": 2040.0},
{"fournisseur": "Transports Lemaire", "numero": "T-77", "date": "2026-09-11", "lignes": [{"libelle": "Livraison régionale", "montant_ht": 800.0}], "total_ht": 800.0, "taux_tva": 20.0, "total_tva": 120.0, "total_ttc": 920.0},
{"fournisseur": "Atelier Rivière", "numero": "F-101", "date": "2026-09-02", "lignes": [{"libelle": "Maintenance des machines", "montant_ht": 1200.0}], "total_ht": 1200.0, "taux_tva": 20.0, "total_tva": 240.0, "total_ttc": 1440.0},
{"fournisseur": "Bureau Fournitures Sud", "numero": "2026-55", "date": "", "lignes": [{"libelle": "Papier et classeurs", "montant_ht": 150.0}], "total_ht": 150.0, "taux_tva": 20.0, "total_tva": 30.0, "total_ttc": 180.0},
{"fournisseur": "Café Service Pro", "numero": "C-3021", "date": "2026-09-22", "lignes": [{"libelle": "Location de machine", "montant_ht": 60.0}, {"libelle": "Capsules", "montant_ht": 45.0}], "total_ht": 120.0, "taux_tva": 20.0, "total_tva": 24.0, "total_ttc": 144.0},
{"fournisseur": "Librairie Technique", "numero": "L-2026-09", "date": "2026-09-30", "lignes": [{"libelle": "Ouvrages professionnels", "montant_ht": 300.0}], "total_ht": 300.0, "taux_tva": 5.5, "total_tva": 16.5, "total_ttc": 316.5}
]La vérificationLe programme de l'étape 2, lancé sur cette lecture :
uv run verifier_lot.pyFacture 1 (F-101) : acceptéeFacture 2 (2026-0412) : acceptéeFacture 3 (8834) : de côté, règle en échec : total = hors taxe + TVAFacture 4 (T-77) : de côté, règle en échec : TVA = hors taxe × tauxFacture 5 (F-101) : de côté, règle en échec : pas déjà reçueFacture 6 (2026-55) : de côté, règle en échec : date présenteFacture 7 (C-3021) : de côté, règle en échec : hors taxe = somme des lignesFacture 8 (L-2026-09) : acceptée3 acceptées, écrites dans acceptees.csv ; 5 mises de côté
Essayé le 7 octobre 2026 avec Python 3.12.7 et Pydantic 2.6.0.
Les cinq anomaliesLe même lot, rangé par le programme. Chaque facture mise de côté échoue sur une seule règle, celle qui dit quoi vérifier.
Acceptées (3)
FACTURE n° F-101 du 2 septembre 2026
Atelier Rivière entreprise fictive
- DésignationMontant HT
- Maintenance des machines1 200,00 €
Total hors taxe1 200,00 €TVA à 20 %240,00 €Total TTC1 440,00 €Acceptée
FACTURE n° 2026-0412 du 15 septembre 2026
Cabinet Exemple entreprise fictive
- DésignationMontant HT
- Conseil, 10 heures1 000,00 €
- Frais de déplacement250,00 €
Total hors taxe1 250,00 €TVA à 20 %250,00 €Total TTC1 500,00 €Acceptée
FACTURE n° L-2026-09 du 30 septembre 2026
Librairie Technique entreprise fictive
- DésignationMontant HT
- Ouvrages professionnels300,00 €
Total hors taxe300,00 €TVA à 5,5 %16,50 €Total TTC316,50 €Acceptée
Mises de côté (5)
FACTURE n° 8834 du 9 septembre 2026
Imprimerie du Port entreprise fictive
- DésignationMontant HT
- Impression de catalogues2 000,00 €
Total hors taxe2 000,00 €TVA à 20 %400,00 €Total TTC2 040,00 €Règle en échec : total = hors taxe + TVA2 000,00 € + 400,00 € font 2 400,00 €, pas 2 040,00 €.
FACTURE n° T-77 du 11 septembre 2026
Transports Lemaire entreprise fictive
- DésignationMontant HT
- Livraison régionale800,00 €
Total hors taxe800,00 €TVA à 20 %120,00 €Total TTC920,00 €Règle en échec : TVA = hors taxe × taux20 % de 800,00 € font 160,00 €, pas 120,00 €.
FACTURE n° F-101 du 2 septembre 2026
Atelier Rivière entreprise fictive
- DésignationMontant HT
- Maintenance des machines1 200,00 €
Total hors taxe1 200,00 €TVA à 20 %240,00 €Total TTC1 440,00 €Règle en échec : pas déjà reçueMême fournisseur, même numéro que la facture 1.
FACTURE n° 2026-55 sans date
Bureau Fournitures Sud entreprise fictive
- DésignationMontant HT
- Papier et classeurs150,00 €
Total hors taxe150,00 €TVA à 20 %30,00 €Total TTC180,00 €Règle en échec : date présenteLa facture ne porte aucune date.
FACTURE n° C-3021 du 22 septembre 2026
Café Service Pro entreprise fictive
- DésignationMontant HT
- Location de machine60,00 €
- Capsules45,00 €
Total hors taxe120,00 €TVA à 20 %24,00 €Total TTC144,00 €Règle en échec : hors taxe = somme des lignes60,00 € + 45,00 € font 105,00 €, pas 120,00 €.
Pourquoi des règles et pas le modèleUn modèle qui recalcule « corrige » sans le dire : la facture 3 deviendrait juste, et l'erreur du fournisseur disparaîtrait. Le programme, lui, montre l'écart et laisse décider une personne.
Extensions
- Les vraies factures : des PDF plutôt que du texte, avec les limites que dit l'extraction des chiffres d'un document.
- D'autres règles : un numéro de TVA intracommunautaire bien formé, un fournisseur connu, une date d'échéance dans les délais légaux, 45 jours fin de mois ou 60 jours au plus.
- La file de validation : un fichier ou un tableau des factures mises de côté, où une personne corrige et relance.
- Mesurer : sur cinquante factures dont vous connaissez la lecture, le nombre de lectures justes ; voir l'évaluation d'un outil d'IA.
Lectures suivantes
Rien avant : c'est un point de départ.
Projet : extraction de factures vers un fichier vérifié
Les pages qui renvoient ici
Limites et sources
La lecture par le modèle suit la documentation d'OpenAI du 7 octobre 2026 et n'a pas encore été exécutée par Meridal ; la vérification a tourné sur la lecture attendue.
Les factures, les fournisseurs et les anomalies sont fabriqués.
- OpenAI, Structured Outputs ; Service Public Entreprendre, délais de paiement.
- Le lot de factures, en Markdown :
apprendre/pages/projets/projet-factures/fichiers/. - L'image d'ouverture : Comble à l'impériale, figures 95 et 96, Encyclopédie de Diderot et d'Alembert, recueil de planches, tome 2, Charpenterie ; domaine public, Wikimedia Commons.
É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