Basalt Studio logo
Basalt Studio.Basalt Studio.
Back

Comment générer des rapports de feuilles de temps avec le nœud Markdown

Eliott Ardisson

Eliott Ardisson

Founder & CEO - Basalt Studio

Updated
tutorials

Apprenez à automatiser vos rapports de feuilles de temps avec n8n et le nœud Markdown : workflow pas à pas, exemples concrets et bonnes pratiques pour les PME.

ai agents
automation
programmatic

Points clés

  • Le nœud Markdown de n8n convertit des données brutes en rapports HTML structurés sans outil tiers coûteux
  • Un workflow bien conçu enchaîne récupération des données, tri, génération Markdown, conversion HTML et export en fichier téléchargeable
  • L’approche est compatible avec tout système proposant une API REST ou un export CSV/JSON : Toggl, Harvest, Clockify, ou un système interne
  • Le CSS injecté dans le document final donne un rendu professionnel directement dans le navigateur, sans dépendance externe
  • Les erreurs les plus fréquentes surviennent lors de la conversion des types de données et de l’ordre d’injection du style dans le HTML

Pourquoi automatiser la production de rapports de feuilles de temps

Si vous gérez une agence, un cabinet de conseil ou une structure de services, vous connaissez ce moment en fin de mois : quelqu’un passe deux heures à consolider des exports, à corriger des cellules mal formatées, à recopier des totaux dans un document Word avant de l’envoyer. C’est répétitif, source d’erreurs, et difficile à déléguer sans perdre en cohérence.

La bonne nouvelle : ce processus est entièrement automatisable avec n8n, sans écrire une seule ligne de backend. Le nœud Markdown, souvent sous-estimé, permet de transformer des données JSON en rapports HTML complets, mis en forme, prêts à être envoyés ou téléchargés.

Ce tutoriel détaille comment construire ce workflow de bout en bout. Il s’adresse aux équipes techniques et aux opérateurs qui souhaitent comprendre la logique avant de déléguer l’implémentation.


Ce que fait le nœud Markdown dans n8n

Le nœud Markdown est un transformateur : il prend une chaîne de texte formatée en Markdown et la convertit en HTML interprétable par un navigateur. C’est un bloc natif de n8n, disponible sans installation supplémentaire.

Ce qui rend ce nœud utile pour la génération de rapports, c’est la combinaison de deux propriétés :

  • La lisibilité du Markdown : vous pouvez construire dynamiquement des tableaux, des titres et des listes en JavaScript sans manipuler du HTML brut, ce qui réduit les erreurs de syntaxe.
  • La flexibilité du HTML de sortie : une fois converti, le HTML peut être enrichi avec du CSS personnalisé, exporté en fichier, envoyé par email ou poussé vers un système de stockage.

Le nœud prend en entrée un champ texte contenant du Markdown valide. Il sort un champ HTML structuré. Le reste du workflow s’occupe de préparer l’entrée et de traiter la sortie.


Architecture du workflow : les huit nœuds essentiels

Avant de détailler chaque étape, voici la structure globale du workflow. Comprendre la séquence logique est aussi important que la configuration de chaque nœud.

NœudRôle
Set / Webhook / HTTP RequestSource des données de temps (données mock ou API réelle)
Function – SortElementsTri des entrées par utilisateur, date, projet
ItemListsExtraction des URLs d’avatar uniques
HTTP RequestTéléchargement des images d’avatar
MergeFusion des données originales avec les binaires image
Function – GenerateMarkdownConstruction de la chaîne Markdown complète
MarkdownConversion Markdown → HTML
Move Binary DataTransformation du HTML en fichier téléchargeable

Chaque nœud a une responsabilité unique. Cette modularité facilite le débogage : si le tableau est mal formaté, le problème est dans le nœud GenerateMarkdown, pas dans le nœud Markdown lui-même.


Étape 1 : Structurer et trier les données sources

La première chose à régler est le format d’entrée. Dans un contexte de production, les données arrivent depuis une API REST (Toggl, Harvest, Clockify, ou un système interne) via un nœud HTTP Request. Pour prototyper, un nœud Set avec des données statiques suffit.

La structure minimale dont vous avez besoin pour chaque entrée :

{
  "user": "Marie Dubois",
  "task": "Revue contractuelle",
  "project": "Client Leroux",
  "date": "2024-03-12",
  "hours": 3.5,
  "avatarUrl": "https://example.com/avatars/marie.jpg"
}

Une fois les données chargées, le nœud Function “SortElements” les réorganise :

const entries = items[0].json.timeEntries;

entries.sort((a, b) => {
  if (a.user !== b.user) return a.user.localeCompare(b.user);
  if (a.date !== b.date) return new Date(a.date) - new Date(b.date);
  return a.project.localeCompare(b.project);
});

return [{ json: { sortedEntries: entries } }];

Le tri par utilisateur en premier est important : la génération Markdown s’appuie sur un regroupement par personne. Si les entrées arrivent mélangées, les sections du rapport seront incohérentes.

Point d’attention : convertissez systématiquement les heures en parseFloat() avant tout calcul. Les APIs renvoient parfois des chaînes de caractères à la place de nombres, ce qui fausse les totaux en silence.


Étape 2 : Récupérer les avatars sans doublons

Les avatars enrichissent le rendu visuel du rapport. Le nœud ItemLists extrait les URLs uniques pour éviter de télécharger plusieurs fois la même image :

const entries = items[0].json.sortedEntries;
const uniqueAvatars = [...new Set(entries.map(e => e.avatarUrl))];
return uniqueAvatars.map(url => ({ json: { avatarUrl: url } }));

Le nœud HTTP Request télécharge ensuite chaque image. Le nœud Merge fusionne les données binaires avec les entrées originales. Cette étape est optionnelle si vos rapports n’ont pas besoin de photos de profil, mais elle change significativement la qualité perçue pour des rapports envoyés à des clients.

Si une URL d’avatar est invalide, ajoutez une validation avant l’appel HTTP :

function validateUrl(url) {
  try {
    new URL(url);
    return url;
  } catch {
    return 'https://example.com/avatars/default.png';
  }
}

Étape 3 : Générer la chaîne Markdown

C’est le nœud central du workflow. Un nœud Function construit dynamiquement la totalité du document Markdown à partir des données triées.

La structure de base d’une section utilisateur :

function generateUserSection(user, entries) {
  const totalHours = entries.reduce((sum, e) => sum + parseFloat(e.hours || 0), 0);

  let section = `## ${user}\n**Total : ${totalHours.toFixed(1)}h**\n\n`;
  section += `| Date | Projet | Tâche | Heures |\n`;
  section += `|------|--------|-------|--------|\n`;

  entries.forEach(e => {
    section += `| ${e.date} | ${e.project} | ${e.task} | ${e.hours}h |\n`;
  });

  section += `\n---\n\n`;
  return section;
}

Ajoutez un en-tête de rapport avec un résumé exécutif avant les sections utilisateur :

let markdown = `# Rapport de feuilles de temps\n`;
markdown += `**Période** : ${startDate} – ${endDate}\n\n`;
markdown += `- Heures totales : **${totalHours.toFixed(1)}h**\n`;
markdown += `- Projets actifs : **${uniqueProjects.length}**\n`;
markdown += `- Collaborateurs : **${uniqueUsers.length}**\n\n---\n\n`;

Le Markdown généré doit être valide : les tableaux ont besoin de la ligne de séparateur (|---|---|), les titres doivent être précédés d’une ligne vide. Ces détails semblent mineurs mais provoquent des rendus cassés si on les néglige.


Étape 4 : Convertir en HTML et injecter le style

Le nœud Markdown prend la chaîne construite à l’étape précédente et la convertit en HTML. Dans la configuration du nœud, activez les options “Tables” et “Breaks” pour que les tableaux Markdown soient correctement rendus.

Le HTML brut sorti par le nœud n’a pas de style. Un nœud Function suivant encapsule le contenu dans un document HTML complet avec CSS :

const htmlContent = items[0].json.htmlReport;

const finalHtml = `<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Rapport de feuilles de temps</title>
  <style>
    body { font-family: 'Segoe UI', Arial, sans-serif; max-width: 960px; margin: 0 auto; padding: 32px; color: #1a1a1a; }
    h1 { font-size: 1.8rem; border-bottom: 2px solid #2c5282; padding-bottom: 8px; }
    h2 { color: #2c5282; margin-top: 2rem; }
    table { border-collapse: collapse; width: 100%; margin: 16px 0; }
    th, td { border: 1px solid #e2e8f0; padding: 10px 14px; text-align: left; }
    th { background-color: #f7fafc; font-weight: 600; }
    @media print { table { page-break-inside: avoid; } h2 { page-break-before: always; } }
  </style>
</head>
<body>
  ${htmlContent}
  <footer style="margin-top: 3rem; font-size: 0.8rem; color: #718096;">
    Généré automatiquement le ${new Date().toLocaleDateString('fr-FR')}
  </footer>
</body>
</html>`;

return [{ json: { finalReport: finalHtml } }];

Le CSS doit être dans le <head>, avant le contenu. C’est l’erreur la plus fréquente : injecter le style après le <body> donne un rendu correct dans certains navigateurs mais cassé à l’impression.


Étape 5 : Exporter le fichier

Le nœud Move Binary Data transforme le HTML en objet binaire téléchargeable. Configuration :

  • Mode : JSON to Binary
  • Source field : finalReport
  • File name : rapport-\{\{ $now.format('yyyy-MM-dd') \}\}.html
  • MIME type : text/html

À partir de là, le fichier peut être envoyé par email via le nœud Gmail ou Outlook, déposé sur Google Drive ou SharePoint, ou retourné comme réponse à un webhook déclenché manuellement.


Pièges courants et comment les éviter

Trois problèmes reviennent régulièrement dans ce type de workflow :

Les totaux incorrects : presque toujours causés par des heures stockées en chaîne de caractères. "3.5" + "2" donne "3.52" en JavaScript, pas 5.5. Utilisez parseFloat() systématiquement.

Les tableaux qui ne s’affichent pas : le nœud Markdown attend du Markdown valide. Un tableau sans ligne de séparateur ou avec des espaces mal placés est ignoré. Testez vos templates Markdown dans un éditeur en ligne avant de les intégrer au workflow.

Les rapports lents sur gros volumes : au-delà de quelques milliers d’entrées, la construction de la chaîne Markdown en mémoire peut ralentir le nœud Function. Traitez les données par lots de 100 à 200 entrées et concaténez les résultats.

Dans notre travail d’accompagnement d’agences de conseil et de cabinets RH sur leurs workflows n8n, le point de friction le plus fréquent n’est pas technique : c’est l’absence de définition claire du format de rapport attendu avant de commencer à coder. Définissez le template visuel d’abord, puis construisez le Markdown qui le génère.


Aller plus loin : notifications et connexion à la facturation

Un workflow de rapports peut facilement être étendu sans changer son cœur.

Alertes sur les données : ajoutez un nœud Function après la génération pour détecter les anomalies (journées supérieures à 10 heures, projets sans activité depuis X jours) et pousser une notification Slack ou par email.

Connexion à la facturation : si votre système de facturation accepte des appels API, le même workflow peut pousser les données de temps directement vers une facture en cours. Chaque entrée de temps devient une ligne de facturation avec quantité, description et taux horaire.

Planification automatique : un nœud Schedule Trigger lance le workflow chaque vendredi à 17h, ou en fin de mois. Le rapport arrive dans les boîtes mail des responsables sans aucune action manuelle.


Checklist de mise en œuvre

Avant de déployer en production, vérifiez ces points :

  • Les données sources sont en JSON valide avec des types cohérents (nombres en nombre, dates en string ISO)
  • Le tri fonctionne correctement avec des prénoms accentués (utilisez localeCompare avec la locale appropriée)
  • Le Markdown généré produit un rendu correct dans un éditeur externe avant d’être passé au nœud
  • Le CSS est injecté dans le <head>, pas après le <body>
  • Le fichier HTML s’ouvre correctement dans Chrome, Firefox et Safari
  • L’impression ou export PDF fonctionne avec les règles @media print
  • Le workflow gère les jeux de données vides sans planter
  • Les tokens API des systèmes sources sont stockés dans les credentials n8n, pas en dur dans les nœuds

Ce que ce workflow ne remplace pas

Cette approche est adaptée à des rapports tabulaires relativement structurés. Elle n’est pas conçue pour des rapports analytiques complexes avec graphiques dynamiques ou pivots, pour lesquels un outil de BI dédié reste plus approprié. Elle est aussi moins adaptée si vos rapports doivent être éditables après génération : le HTML produit est un document de présentation, pas un tableur.

Pour les usages couverts — consolidation hebdomadaire ou mensuelle, rapport client, suivi d’équipe — c’est une des approches les plus légères et les plus maintenables disponibles sur n8n.


Automatiser la production de rapports de feuilles de temps avec le nœud Markdown de n8n est un projet de quelques jours pour une équipe technique, et quelques semaines si c’est votre premier workflow n8n. Le résultat est un processus qui tourne sans intervention, produit des documents cohérents et s’adapte à l’évolution de vos données sans réécriture complète.

Si vous souhaitez discuter de l’application de cette approche à votre contexte spécifique — intégration avec votre système de temps existant, format de rapport sur mesure, ou connexion à votre outil de facturation — vous pouvez réserver un appel stratégie IA avec Basalt Studio.