Automatiser un reporting mensuel en Python : le guide étape par étape

Un cas concret, un reporting de dépenses par centre de coûts, construit étape par étape jusqu'au script complet.

7 min de lecture

Un reporting mensuel construit à la main à partir de plusieurs exports Excel suit presque toujours la même mécanique : lire les fichiers, les assembler, les croiser avec une référence, calculer un résultat, vérifier qu'il est cohérent, puis le livrer dans un format partageable. Cette mécanique, une fois écrite en Python, se rejoue à l'identique chaque mois, en quelques secondes plutôt qu'en plusieurs heures.

Voici comment construire ce script étape par étape, à partir d'un cas concret : un reporting de dépenses par centre de coûts.

Le scénario

Chaque mois, plusieurs fichiers de commandes arrivent dans un dossier partagé, un par semaine ou par système source. Un fichier séparé fait la correspondance entre les codes de centre de coûts et leur nom lisible, utilisé pour la présentation du rapport. Un troisième fichier liste le budget alloué à chaque centre de coûts. L'objectif : produire un rapport consolidé des dépenses par centre de coûts, fiable, avec une alerte automatique si l'un d'eux dépasse son budget.

1Lire tous les fichiers nécessaires

python
import pandas as pd
from pathlib import Path

dossier_source = Path("commandes_mensuelles")
df_commandes = pd.concat([pd.read_excel(f) for f in dossier_source.glob("*.xlsx")], ignore_index=True)

df_centres_de_cout = pd.read_excel("referentiel_centres_de_cout.xlsx")
df_budgets = pd.read_excel("budgets_centres_de_cout.xlsx")

Toutes les lectures de fichiers sont regroupées ici, au tout début du script : ça permet de voir en un coup d'œil toutes les sources utilisées, sans avoir à parcourir le script pour les retrouver.

2Vérifier la qualité des données

Avant d'aller plus loin, mieux vaut vérifier qu'aucune ligne n'est dupliquée par erreur, un fichier importé deux fois par exemple :

python
if df_commandes.duplicated().any():
    print("Attention : des lignes dupliquées ont été détectées.")

Ce contrôle prend une ligne, et évite qu'une erreur de ce type ne passe inaperçue dans le reste du traitement.

3Croiser avec le référentiel des centres de coûts

python
df_commandes_enrichies = df_commandes.merge(
    df_centres_de_cout,
    on="id_centre_de_cout",
    how="left",
    validate="many_to_one",
)

Le paramètre validate="many_to_one" garantit que chaque code de centre de coûts est bien unique dans le référentiel. S'il ne l'est pas, une erreur explicite est levée avant même de produire un résultat, plutôt qu'un chiffre silencieusement faux — le même principe que celui détaillé dans notre article sur les doublons silencieux d'une RECHERCHEV.

4Agréger les dépenses

python
df_rapport = (
    df_commandes_enrichies
    .groupby("nom_centre_de_cout", as_index=False)
    .agg({"montant": "sum"})
)

Cette agrégation reprend en une ligne ce que ferait un tableau croisé dynamique sous Excel, avec un avantage direct : le résultat reste un DataFrame comme un autre, qu'on peut enrichir librement à l'étape suivante, sans passer par un outil distinct comme Power Pivot (voir notre article dédié aux tableaux croisés dynamiques en pandas).

5Ajouter une alerte budgétaire

python
df_rapport = df_rapport.merge(df_budgets, on="nom_centre_de_cout", how="left")

for _, ligne in df_rapport.iterrows():
    if ligne["montant"] > ligne["budget"]:
        print(f"Alerte : {ligne['nom_centre_de_cout']} dépasse son budget ({ligne['montant']} € pour {ligne['budget']} € prévus).")

Cette vérification, difficile à automatiser nativement dans un tableau croisé dynamique ou dans Power Query (voir aussi notre comparatif Python vs VBA), tient ici en trois lignes.

6Exporter le résultat

python
from datetime import date

aujourdhui = date.today()
annee, mois = aujourdhui.year, aujourdhui.month - 1

if mois == 0:
    mois = 12
    annee = annee - 1

nom_fichier = f"rapport_mensuel_{annee}-{mois:02d}.xlsx"
df_rapport.to_excel(nom_fichier, index=False, sheet_name="Synthèse")

Le nom du fichier reprend le mois et l'année concernés par le reporting, celui du mois précédent, en supposant que le reporting du mois M se fait en général au tout début du mois M+1. Le fichier obtenu est un Excel classique, que n'importe quel collègue peut ouvrir, filtrer ou présenter, sans avoir besoin de connaître Python.

Rendre ce script réutilisable d'un mois sur l'autre

Ce script fonctionne tel quel, mais gagne à être structuré un minimum pour être repris facilement : quelques commentaires expliquant le rôle de chaque bloc, et un nommage cohérent des fichiers de sortie, ici par mois et année, plutôt que d'écraser le même fichier à chaque exécution. Ces quelques précautions font toute la différence entre un script utilisé une fois et un script réellement adopté dans la durée.

Le script complet

python
import pandas as pd
from pathlib import Path
from datetime import date

# --- Lecture de tous les fichiers ---
dossier_source = Path("commandes_mensuelles")
df_commandes = pd.concat(
    [pd.read_excel(f) for f in dossier_source.glob("*.xlsx")],
    ignore_index=True,
)
df_centres_de_cout = pd.read_excel("referentiel_centres_de_cout.xlsx")
df_budgets = pd.read_excel("budgets_centres_de_cout.xlsx")

# --- Contrôle qualité ---
if df_commandes.duplicated().any():
    print("Attention : des lignes dupliquées ont été détectées.")

# --- Croisement avec le référentiel ---
df_commandes_enrichies = df_commandes.merge(
    df_centres_de_cout, on="id_centre_de_cout", how="left", validate="many_to_one"
)

# --- Agrégation ---
df_rapport = (
    df_commandes_enrichies
    .groupby("nom_centre_de_cout", as_index=False)
    .agg({"montant": "sum"})
)

# --- Alerte budgétaire ---
df_rapport = df_rapport.merge(df_budgets, on="nom_centre_de_cout", how="left")

for _, ligne in df_rapport.iterrows():
    if ligne["montant"] > ligne["budget"]:
        print(f"Alerte : {ligne['nom_centre_de_cout']} dépasse son budget.")

# --- Export, nommé selon le mois précédent ---
aujourdhui = date.today()
annee, mois = aujourdhui.year, aujourdhui.month - 1

if mois == 0:
    mois = 12
    annee = annee - 1

nom_fichier = f"rapport_mensuel_{annee}-{mois:02d}.xlsx"
df_rapport.to_excel(nom_fichier, index=False, sheet_name="Synthèse")

En résumé

Retrouvez l'ensemble de nos articles sur Python pour les métiers.

Questions fréquentes
Non, c'est justement l'intérêt : une fois écrit, il se rejoue à l'identique sur les nouveaux fichiers, sans modification.
Le script devra être ajusté à ce moment-là, mais cette maintenance reste ponctuelle, sans commune mesure avec un traitement manuel refait chaque mois.
En partie, mais avec certaines limites, notamment sur l'étape d'alerte budgétaire, qui sort du cadre de ce que ces outils permettent nativement (voir nos comparatifs dédiés à Power Query et à VBA).

Vous voulez apprendre à construire ce type de script vous-même, adapté à votre reporting ?

Découvrir Python pour les métiers →