Aller au contenu

S'exercerNiveau 3, Construire30 minutes

Projet : extraction de factures vers un fichier vérifié

Un lot de huit factures fabriquées, lues par un programme, vérifiées par des règles, puis rangées : les justes dans un fichier, les autres de côté, chacune avec la règle en échec. La chaîne d'une extraction qu'on peut confier à un programme.

Écrit le 7 octobre 2026 · Vérifié le 7 octobre 2026

Vous saurez

  • Lire un lot de documents avec un format imposé
  • Vérifier chaque lecture par des règles : calculs, date, doublons
  • Ranger les justes dans un fichier, et mettre les autres de côté
  • Dire pourquoi une facture est mise de côté

Dans le parcours

Premiers pas en développement

Pour finir le parcours

Aussi dans : Progression par projets

Sur cette page
  1. L'essentiel
  2. Objectif
  3. Données fournies
  4. Consignes
  5. Critères de réussite
  6. Indications
  7. Correction commentée
  8. Extensions
  9. Lectures suivantes
L'essentiel
  1. Trois temps : lire chaque facture avec un format imposé, la vérifier par des règles, la ranger.
  2. Le modèle recopie ; le programme calcule et juge. Aucun montant n'est calculé par le modèle.
  3. 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.
  4. Les doublons et les dates absentes se voient par des règles aussi simples que les calculs.
  5. Le fichier final ne contient que ce qui a passé toutes les règles.
1

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.

2

Données fournies

  • Lot de huit factures fournisseursTexte Markdown, 2 Ko · septembre 2026, recopiées en texte
    Télécharger
  1. 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 €
  2. 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 €
  3. 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 €
  4. 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 €
  5. 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 €
  6. 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 €
  7. 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 €
  8. 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 €
Les huit factures du lot, telles qu'elles arrivent. Fabriquées pour l'exercice : les fournisseurs, les numéros et les montants sont inventés. Avant de lancer l'étape 2, comptez celles qui passeront les cinq règles ; la correction dit lesquelles.

Aussi en Word, pour l'essayer dans un assistant : lot-de-factures.docx.

3

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.py
from 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.py
import 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")
La sortieuv run lire_lot.py

Pas 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.py
import 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é")
4

Critères de réussite

5

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.

6

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 :

La sortieuv run verifier_lot.py
Facture 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)

  1. 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

  2. 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

  3. 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)

  1. 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 €.

  2. 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 €.

  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 €

    Règle en échec : pas déjà reçueMême fournisseur, même numéro que la facture 1.

  4. 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.

  5. 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 €.

Les cercles et les calculs viennent des cinq règles, appliquées au lot par le script qui écrit cette page ; les factures restent fabriquées.

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.

7

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.
8

Lectures suivantes

↑ Plus général

Rien avant : c'est un point de départ.

Vous êtes ici

Projet : extraction de factures vers un fichier vérifié

Tout le dossier « Projets d'entraînement »

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.

  1. OpenAI, Structured Outputs ; Service Public Entreprendre, délais de paiement.
  2. Le lot de factures, en Markdown : apprendre/pages/projets/projet-factures/fichiers/.
  3. 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.

Service correspondant

Préparer les documents qui reviennent

1 650 € à 5 100 €, puis sur devis

Voir le service

Un premier échange de 30 minutes

En visio et sans frais : vous décrivez votre situation, nous vous indiquons ce qui est réalisable, avec quels outils et pour quel ordre de prix. Vous pouvez aussi nous écrire ; chaque message reçoit une réponse écrite.