AutomationFlowsGeneral › Schulung: Daten-pipeline → E-mail Report

Schulung: Daten-pipeline → E-mail Report

Schulung: Daten-Pipeline → E-Mail Report. Uses emailSend. Event-driven trigger; 5 nodes.

Event trigger★★★★☆ complexity5 nodesEmail Send
General Trigger: Event Nodes: 5 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Schulung: Daten-Pipeline \u2192 E-Mail Report",
  "nodes": [
    {
      "parameters": {},
      "id": "trigger-1",
      "name": "1. Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        200,
        300
      ],
      "notes": "SCHULUNG: Manueller Start fuer Demo-Zwecke. In Produktion durch Schedule Trigger (z.B. jeden 5. Werktag) oder Webhook ersetzen."
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// SCHULUNG: Beispieldaten generieren\n// In Produktion wird dieser Node durch echte Datenquelle ersetzt\n// ============================================================\n\nconst guv_data = [\n  { Kostenart: 'Umsatz Inland', Plan: 5200000, Ist: 5450000, Typ: 'Revenue' },\n  { Kostenart: 'Umsatz Export', Plan: 3800000, Ist: 3620000, Typ: 'Revenue' },\n  { Kostenart: 'Umsatz Gesamt', Plan: 9000000, Ist: 9070000, Typ: 'Revenue' },\n  { Kostenart: 'Umsatz Marge %', Plan: 0.42, Ist: 0.41, Typ: 'Percentage' },\n  { Kostenart: 'Materialkosten', Plan: 2700000, Ist: 2850000, Typ: 'Cost' },\n  { Kostenart: 'Personalkosten', Plan: 3100000, Ist: 3050000, Typ: 'Cost' },\n  { Kostenart: 'Sonstige Kosten', Plan: 800000, Ist: 920000, Typ: 'Cost' },\n  { Kostenart: 'Kosten Marge %', Plan: 0.73, Ist: 0.75, Typ: 'Percentage' },\n  { Kostenart: 'Abschreibungen', Plan: 450000, Ist: 440000, Typ: 'Cost' },\n  { Kostenart: 'EBIT', Plan: 1950000, Ist: 1810000, Typ: 'Result' }\n];\n\nreturn guv_data.map(row => ({ json: row }));"
      },
      "id": "demo-data-1",
      "name": "2. Demo-Daten erzeugen",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        300
      ],
      "notes": "SCHULUNG: Erzeugt Beispiel-GuV-Daten. Zeigt typische D365-Exportstruktur inkl. Prozentzeilen (die spaeter gefiltert werden muessen). Dieser Node entfaellt in Produktion."
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// SCHULUNG: Datentransformation & Bereinigung\n// Kernlogik einer Controlling-Pipeline\n// ============================================================\n\nconst items = $input.all();\n\n// SCHRITT 1: Prozentzeilen ausfiltern\nconst filtered = items.filter(item => item.json.Typ !== 'Percentage');\n\n// SCHRITT 2: Abweichung berechnen\nconst enriched = filtered.map(item => {\n  const d = item.json;\n  let abweichung;\n  \n  // WICHTIG: Vorzeichenkonvention!\n  // Revenue: Ist > Plan = positiv (gut)\n  // Kosten:  Ist > Plan = negativ (schlecht)\n  if (d.Typ === 'Revenue' || d.Typ === 'Result') {\n    abweichung = d.Ist - d.Plan;\n  } else {\n    abweichung = -(d.Ist - d.Plan);\n  }\n  \n  const abweichung_pct = d.Plan !== 0 ? (abweichung / Math.abs(d.Plan)) * 100 : 0;\n  \n  let ampel;\n  if (abweichung_pct >= 2) ampel = 'gruen';\n  else if (abweichung_pct >= -2) ampel = 'gelb';\n  else ampel = 'rot';\n  \n  return {\n    json: {\n      ...d,\n      Abweichung_EUR: abweichung,\n      Abweichung_Pct: Math.round(abweichung_pct * 10) / 10,\n      Ampel: ampel\n    }\n  };\n});\n\nreturn enriched;"
      },
      "id": "transform-1",
      "name": "3. Daten transformieren",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ],
      "notes": "SCHULUNG: Zentrale Transformationslogik.\n1. Prozentzeilen rausfiltern (typischer D365-Fallstrick)\n2. Abweichung berechnen mit korrekter Vorzeichenkonvention\n3. Ampelbewertung (Rot/Gelb/Gruen) hinzufuegen"
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// SCHULUNG: HTML-Report generieren\n// Management-tauglicher E-Mail-Report\n// ============================================================\n\nconst items = $input.all();\nconst now = new Date();\nconst dateStr = now.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });\n\nconst ampelColor = { 'gruen': '#27ae60', 'gelb': '#f39c12', 'rot': '#e74c3c' };\n\nfunction fmtEUR(val) {\n  return new Intl.NumberFormat('de-DE', { maximumFractionDigits: 0 }).format(val);\n}\nfunction fmtPct(val) {\n  return val.toFixed(1).replace('.', ',');\n}\n\nlet tableRows = '';\nfor (const item of items) {\n  const d = item.json;\n  const color = ampelColor[d.Ampel] || '#95a5a6';\n  const sign = d.Abweichung_EUR >= 0 ? '+' : '';\n  tableRows += `<tr>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;font-weight:${d.Typ==='Result'?'bold':'normal'}\">${d.Kostenart}</td>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;text-align:right\">${fmtEUR(d.Plan)}</td>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;text-align:right\">${fmtEUR(d.Ist)}</td>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:${d.Abweichung_EUR>=0?'#27ae60':'#e74c3c'}\">${sign}${fmtEUR(d.Abweichung_EUR)}</td>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;text-align:right\">${sign}${fmtPct(d.Abweichung_Pct)} %</td>\n    <td style=\"padding:8px 12px;border-bottom:1px solid #eee;text-align:center\"><span style=\"display:inline-block;width:16px;height:16px;border-radius:50%;background:${color}\"></span></td>\n  </tr>`;\n}\n\nconst ebit = items.find(i => i.json.Kostenart === 'EBIT');\nconst ebitAbw = ebit ? ebit.json.Abweichung_EUR : 0;\nconst ebitAmpel = ebit ? ebit.json.Ampel : 'gelb';\n\nconst html = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head>\n<body style=\"font-family:'Segoe UI',Arial,sans-serif;color:#2c3e50;max-width:800px;margin:0 auto;padding:20px;\">\n  <div style=\"background:linear-gradient(135deg,#2c3e50,#3498db);color:white;padding:24px 32px;border-radius:8px 8px 0 0;\">\n    <h1 style=\"margin:0;font-size:22px;\">Monatlicher Controlling-Report</h1>\n    <p style=\"margin:4px 0 0 0;opacity:0.85;font-size:14px;\">Automatisch erstellt am ${dateStr} | Unternehmen A</p>\n  </div>\n  <div style=\"background:#f8f9fa;padding:20px 32px;border-left:1px solid #e0e0e0;border-right:1px solid #e0e0e0;\">\n    <h2 style=\"font-size:16px;margin:0 0 12px 0;\">Executive Summary</h2>\n    <ul style=\"margin:0;padding-left:20px;line-height:1.8;\">\n      <li>Umsatz leicht ueber Plan (+0,8%) \u2014 Inland kompensiert Exportrueckgang</li>\n      <li>Materialkosten ueber Budget (+150 kEUR) \u2014 Preiseffekt Halbleiter</li>\n      <li>Personalkosten unter Plan (-50 kEUR) \u2014 offene Stelle BU Engineering</li>\n      <li><strong>EBIT ${fmtEUR(Math.abs(ebitAbw))} EUR ${ebitAbw>=0?'ueber':'unter'} Plan</strong> \u2014 Ampel: <span style=\"color:${ampelColor[ebitAmpel]};font-weight:bold;\">${ebitAmpel.toUpperCase()}</span></li>\n      <li>Handlungsbedarf: Materialkostenentwicklung im naechsten RF adressieren</li>\n    </ul>\n  </div>\n  <div style=\"padding:20px 32px;border:1px solid #e0e0e0;border-top:none;\">\n    <h2 style=\"font-size:16px;margin:0 0 12px 0;\">Plan-Ist-Vergleich (kumuliert YTD)</h2>\n    <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n      <thead><tr style=\"background:#2c3e50;color:white;\">\n        <th style=\"padding:10px 12px;text-align:left;\">Position</th>\n        <th style=\"padding:10px 12px;text-align:right;\">Plan (EUR)</th>\n        <th style=\"padding:10px 12px;text-align:right;\">Ist (EUR)</th>\n        <th style=\"padding:10px 12px;text-align:right;\">Abw. (EUR)</th>\n        <th style=\"padding:10px 12px;text-align:right;\">Abw. (%)</th>\n        <th style=\"padding:10px 12px;text-align:center;\">Status</th>\n      </tr></thead>\n      <tbody>${tableRows}</tbody>\n    </table>\n  </div>\n  <div style=\"background:#f8f9fa;padding:16px 32px;border:1px solid #e0e0e0;border-top:none;border-radius:0 0 8px 8px;font-size:12px;color:#7f8c8d;\">\n    <p style=\"margin:0;\">Automatisch generiert via n8n | Datenquelle: D365 (simuliert) | Schulungsbeispiel</p>\n  </div>\n</body></html>`;\n\nreturn [{ json: { html_report: html, subject: `Controlling-Report ${dateStr} | Unternehmen A`, date: dateStr } }];"
      },
      "id": "report-gen-1",
      "name": "4. HTML-Report generieren",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ],
      "notes": "SCHULUNG: Erzeugt Management-tauglichen HTML-Report mit Executive Summary, Plan-Ist-Tabelle und Ampelbewertung."
    },
    {
      "parameters": {
        "fromEmail": "controlling@unternehmen-a.de",
        "toEmail": "={{ $json.toEmail || 'cfo@unternehmen-a.de' }}",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "html": "={{ $json.html_report }}",
        "options": {}
      },
      "id": "email-send-1",
      "name": "5. Report per E-Mail senden",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1140,
        300
      ],
      "notes": "SCHULUNG: Versendet HTML-Report per SMTP. Voraussetzung: SMTP-Credentials einrichten. Alternativen: Gmail, Outlook, SendGrid Node.",
      "credentials": {}
    }
  ],
  "connections": {
    "1. Manual Trigger": {
      "main": [
        [
          {
            "node": "2. Demo-Daten erzeugen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Demo-Daten erzeugen": {
      "main": [
        [
          {
            "node": "3. Daten transformieren",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. Daten transformieren": {
      "main": [
        [
          {
            "node": "4. HTML-Report generieren",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. HTML-Report generieren": {
      "main": [
        [
          {
            "node": "5. Report per E-Mail senden",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "Schulung"
    },
    {
      "name": "Controlling"
    },
    {
      "name": "Daten-Pipeline"
    }
  ],
  "meta": {
    "templateCredsSetupCompleted": true
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Schulung: Daten-Pipeline → E-Mail Report. Uses emailSend. Event-driven trigger; 5 nodes.

Source: https://github.com/Michael-Braun72/Control4Insights_Code/blob/4cb3d18042a738e0c76f6324cb822dc3dafb2547/output/n8n_workflows/Schulung_Daten_Pipeline_Workflow.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

SwitchSubTask. Uses graphql, n8n-nodes-switch-nine-thousand, executeWorkflowTrigger, emailSend. Event-driven trigger; 30 nodes.

GraphQL, N8N Nodes Switch Nine Thousand, Execute Workflow Trigger +1
General

This workflow automatically handles errors in your n8n workflows by: Detecting when an error occurs and capturing the error details Sending an email notification with the error message and affected no

Error Trigger, Email Send, n8n
General

Perfect for disaster recovery or migrating between environments, this workflow automatically identifies your most recent FTP backup and provides a manual restore capability that intelligently excludes

Email Send, Ftp, Read Write File +2
General

Perfect for disaster recovery or migrating between environments, this workflow automatically identifies your most recent backup and provides a manual restore capability that intelligently excludes the

Email Send, Execute Command, Stop And Error
General

Find A New Book Recommendations. Uses manualTrigger, httpRequest, emailSend. Event-driven trigger; 13 nodes.

HTTP Request, Email Send