{
  "name": "IbetMax · Angebotsanfrage Marktforschung (BPMN-Soll-Prozess)",
  "nodes": [
    {
      "parameters": {
        "content": "## 1 · Eingang & Anonymax\nAnfrage kommt per Mail, Anhang wird gelesen, **Anonymax** (lokal, IbetMax) maskiert Kundendaten. Die Zuordnungstabelle bleibt lokal. Danach erstellt die KI ein strukturiertes Factsheet.\n\n**Upgrade lokale KI:** Anonymax entfällt, die Modell-Nodes zeigen auf Ollama.",
        "width": 880,
        "height": 380,
        "color": 5
      },
      "id": "99ec1a52-a7eb-4004-91be-b2f709d1a1f1",
      "name": "Notiz 1 – Eingang",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        120
      ]
    },
    {
      "parameters": {
        "content": "## 2 · Parallele Vorarbeit (BPMN: paralleles Gateway)\n**Oben:** Recherche-Agent – Inzidenzen und Marktannahmen mit Quelle. Tools ergänzen: Institutsgedächtnis (Postgres/pgvector) und Websuche.\n\n**Unten:** Kapazitätsprüfung – Bedarf ableiten (Tablets, Interviewertage, GD-Räume), gegen Ressourcenkalender prüfen, Ampel. Nur bei Gelb/Rot geht eine Machbarkeitsanfrage an die Fachabteilung (Timeout 24 h = Timer-Grenzereignis).",
        "width": 1100,
        "height": 760,
        "color": 6
      },
      "id": "6324ea70-3afb-4358-a33b-ab7c7e0e67cd",
      "name": "Notiz 2 – Parallel",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        880,
        -60
      ]
    },
    {
      "parameters": {
        "content": "## 3 · Kalkulation & Human in the Loop\nDie KI liefert Annahmen, **gerechnet wird mit Code** gegen die Preismatrix – nachvollziehbar und reproduzierbar.\n\nDer Client Service bekommt alles in einer Prüfmail mit Formular: Freigeben, Korrigieren oder Ablehnen (BPMN: exklusives Gateway).",
        "width": 900,
        "height": 520,
        "color": 4
      },
      "id": "5bf37b9f-602f-498c-a60b-44e52cd5f0c2",
      "name": "Notiz 3 – Mensch",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2000,
        120
      ]
    },
    {
      "parameters": {
        "content": "## 4 · Angebot, Versand, Ablage\nErst jetzt setzt Anonymax die echten Kundendaten lokal wieder ein. Das Angebot wird als PDF gerendert und als **Mail-Entwurf** angelegt – der Mensch drückt auf Senden. Nach 7 Tagen kommt eine Nachfass-Erinnerung.",
        "width": 1340,
        "height": 520,
        "color": 7
      },
      "id": "fc09f85d-f307-462d-b35d-4c6b22bda3f7",
      "name": "Notiz 4 – Angebot",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2920,
        120
      ]
    },
    {
      "parameters": {
        "mailbox": "INBOX",
        "postProcessAction": "read",
        "downloadAttachments": true,
        "options": {}
      },
      "id": "6ad02acc-380c-451c-812f-2402988f0cd1",
      "name": "Anfrage-Postfach",
      "type": "n8n-nodes-base.emailReadImap",
      "typeVersion": 2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "operation": "pdf",
        "binaryPropertyName": "attachment_0",
        "options": {}
      },
      "id": "b74d9737-d721-42b4-a8cf-4d12d95e750a",
      "name": "Anhang auslesen",
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://anonymax.local:8080/api/v1/anonymize",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ text: ($json.text || '') + '\\n\\n' + ($('Anfrage-Postfach').item.json.textPlain || ''), profil: 'marktforschung', mapping_lokal_speichern: true }) }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a806cd70-a08b-4c82-aaed-5e1d3c0117ab",
      "name": "Anonymax: maskieren",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": true,
        "text": "=Du bist Projektleiter in einem Marktforschungsinstitut. Erstelle aus der folgenden anonymisierten Kundenanfrage ein Factsheet. Erfinde nichts: Was nicht in der Anfrage steht, bleibt leer oder landet unter offene_fragen.\n\n{{ $json.text_anonymisiert }}"
      },
      "id": "9b649137-a67f-40d2-b5c7-2302871341f9",
      "name": "Factsheet erstellen",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        660,
        300
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5-5",
          "mode": "id"
        },
        "options": {
          "temperature": 0
        }
      },
      "id": "bd18a154-73d8-4e01-bd3c-0ab78ddba29a",
      "name": "Claude (Factsheet)",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        600,
        520
      ]
    },
    {
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"kunde_platzhalter\": {\n      \"type\": \"string\"\n    },\n    \"methode\": {\n      \"type\": \"string\",\n      \"description\": \"z. B. CAPI, CATI, Online, Gruppendiskussion\"\n    },\n    \"zielgruppe\": {\n      \"type\": \"string\"\n    },\n    \"fallzahl\": {\n      \"type\": \"number\"\n    },\n    \"anzahl_gruppendiskussionen\": {\n      \"type\": \"number\"\n    },\n    \"regionen\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"feldbeginn\": {\n      \"type\": \"string\",\n      \"description\": \"ISO-Datum oder leer\"\n    },\n    \"feldzeit_tage\": {\n      \"type\": \"number\",\n      \"description\": \"reine Feldzeit in Werktagen\"\n    },\n    \"deadline\": {\n      \"type\": \"string\",\n      \"description\": \"ISO-Datum der Ergebnislieferung\"\n    },\n    \"deliverables\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"interviewdauer_min\": {\n      \"type\": [\n        \"number\",\n        \"null\"\n      ],\n      \"description\": \"Interviewlänge in Minuten\"\n    },\n    \"inzidenz_laut_kunde\": {\n      \"type\": [\n        \"number\",\n        \"null\"\n      ],\n      \"description\": \"0 bis 1, nur wenn genannt\"\n    },\n    \"offene_fragen\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    }\n  },\n  \"required\": [\n    \"methode\",\n    \"zielgruppe\",\n    \"fallzahl\",\n    \"deadline\",\n    \"offene_fragen\"\n  ]\n}"
      },
      "id": "25fce13d-572f-4b5a-9cd2-5538828457f7",
      "name": "Factsheet-Schema",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.2,
      "position": [
        760,
        520
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO anfragen (eingang, factsheet, prioritaet, status)\nVALUES (now(), $1::jsonb, $2, 'neu')\nRETURNING id;",
        "options": {
          "queryReplacement": "={{ JSON.stringify($json.output) }},{{ (new Date($json.output.deadline) - new Date()) / 86400000 < 14 ? 'hoch' : 'normal' }}"
        }
      },
      "id": "1cebc9d1-83eb-4bdf-8438-076a36839647",
      "name": "Anfrage speichern",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Factsheet:\n{{ JSON.stringify($('Factsheet erstellen').item.json.output, null, 2) }}\n\nRecherchiere die Annahmen, die für die Kalkulation nötig sind – vor allem die Inzidenz der Zielgruppe. Antworte ausschließlich als JSON: {\"inzidenz\": 0.0-1.0, \"sicherheit\": \"hoch|mittel|niedrig\", \"quellen\": [\"...\"], \"weitere_annahmen\": [\"...\"]}",
        "options": {
          "systemMessage": "Du bist Research-Analyst. Prüfe zuerst das Institutsgedächtnis (frühere Studien), dann externe Quellen. Nenne zu jeder Zahl eine Quelle. Wenn du unsicher bist, sag es."
        }
      },
      "id": "af96ff24-28fc-422a-91ea-ba4657de8e19",
      "name": "Recherche-Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        1160,
        100
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5-5",
          "mode": "id"
        },
        "options": {
          "temperature": 0.2
        }
      },
      "id": "c936cee4-c9cc-49ce-ad81-22fdcb88563d",
      "name": "Claude (Recherche)",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        1160,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// KI-Annahmen in harte Ressourcen übersetzen (Beispielregeln)\nconst fs = $('Factsheet erstellen').first().json.output;\nconst methode = (fs.methode || '').toLowerCase();\nconst n = fs.fallzahl || 0;\nconst feldtage = fs.feldzeit_tage || 30; // Werktage\nconst gruppen = fs.anzahl_gruppendiskussionen || 0;\n\nconst INTERVIEWS_PRO_TABLET_TAG = 4;\nconst INTERVIEWS_PRO_INTERVIEWER_TAG = 6;\n\nconst bedarf = {\n  tablets: methode.includes('capi') ? Math.ceil(n / (feldtage * INTERVIEWS_PRO_TABLET_TAG)) : 0,\n  interviewertage: methode.match(/capi|cati/) ? Math.ceil(n / INTERVIEWS_PRO_INTERVIEWER_TAG) : 0,\n  gd_raum_tage: Math.ceil(gruppen / 2),\n};\nbedarf.interviewer_parallel = Math.ceil(bedarf.interviewertage / feldtage);\n\nconst start = fs.feldbeginn || new Date().toISOString().slice(0, 10);\nconst ende = new Date(new Date(start).getTime() + Math.ceil(feldtage * 7 / 5) * 86400000).toISOString().slice(0, 10);\nreturn [{ json: { bedarf, start, ende, deadline: fs.deadline } }];"
      },
      "id": "a39a2c8f-fb6b-40d8-a4f3-d88543f798aa",
      "name": "Ressourcenbedarf ableiten",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1160,
        500
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT r.typ,\n       MIN(r.bestand - COALESCE(b.gebucht, 0)) AS min_frei\nFROM ressourcen r\nCROSS JOIN generate_series($1::date, $2::date, '1 day') AS tag\nLEFT JOIN (\n  SELECT ressource_id, datum, SUM(menge) AS gebucht\n  FROM buchungen GROUP BY ressource_id, datum\n) b ON b.ressource_id = r.id AND b.datum = tag::date\nGROUP BY r.typ;\n\n-- zusätzlich: wichtige Deadlines im Zeitraum\n-- SELECT * FROM termine WHERE datum BETWEEN $1 AND $3 AND wichtig;",
        "options": {
          "queryReplacement": "={{ $json.start }},{{ $json.ende }},{{ $json.deadline }}"
        }
      },
      "id": "e9c10d4b-a020-4fb6-9b7d-fec86692f293",
      "name": "Ressourcenkalender prüfen",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1380,
        500
      ]
    },
    {
      "parameters": {
        "jsCode": "const bedarf = $('Ressourcenbedarf ableiten').first().json.bedarf;\nconst frei = Object.fromEntries($input.all().map(i => [i.json.typ, Number(i.json.min_frei)]));\n\nconst pruefe = (typ, benoetigt) => {\n  if (!benoetigt) return { typ, benoetigt, frei: frei[typ] ?? null, ampel: 'gruen' };\n  const f = frei[typ] ?? 0;\n  const ampel = f >= benoetigt * 1.2 ? 'gruen' : f >= benoetigt ? 'gelb' : 'rot';\n  return { typ, benoetigt, frei: f, ampel };\n};\n\nconst details = [\n  pruefe('tablet', bedarf.tablets),\n  pruefe('fulltimer', bedarf.interviewer_parallel),\n  pruefe('gd_raum', bedarf.gd_raum_tage ? 1 : 0),\n];\nconst rang = { gruen: 0, gelb: 1, rot: 2 };\nconst ampel = details.reduce((a, d) => rang[d.ampel] > rang[a] ? d.ampel : a, 'gruen');\nreturn [{ json: { ampel, details } }];"
      },
      "id": "bf45ab31-756d-4264-be6d-6d2b4a597c2f",
      "name": "Ampel bilden",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1600,
        500
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "459aabb0-2e50-4985-8746-b6e3d3b9c071",
              "leftValue": "={{ $json.ampel }}",
              "rightValue": "gruen",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "5cd4db3f-7399-4d4f-8dfe-3656ae546a99",
      "name": "Engpass?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1820,
        500
      ]
    },
    {
      "parameters": {
        "operation": "sendAndWait",
        "sendTo": "fachabteilung@institut.example",
        "subject": "=Machbarkeit prüfen: {{ $('Factsheet erstellen').item.json.output.methode }}, n={{ $('Factsheet erstellen').item.json.output.fallzahl }}",
        "message": "=Ampel: {{ $json.ampel.toUpperCase() }}\n\n{{ $json.details.map(d => `${d.typ}: benötigt ${d.benoetigt}, frei ${d.frei} → ${d.ampel}`).join('\\n') }}\n\nBitte kurz bestätigen.",
        "responseType": "customForm",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Machbar?",
              "fieldType": "dropdown",
              "requiredField": true,
              "fieldOptions": {
                "values": [
                  {
                    "option": "Ja"
                  },
                  {
                    "option": "Eingeschränkt"
                  },
                  {
                    "option": "Nein"
                  }
                ]
              }
            },
            {
              "fieldLabel": "Kommentar",
              "fieldType": "textarea"
            }
          ]
        },
        "options": {
          "limitWaitTime": {
            "values": {
              "limitType": "afterTimeInterval",
              "resumeAmount": 24,
              "resumeUnit": "hours"
            }
          }
        }
      },
      "id": "4d98ee45-4515-4759-b0f9-a03ca31f465b",
      "name": "Machbarkeit anfragen",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2040,
        400
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "id": "cac7a82a-c076-4777-a220-b13704708340",
      "name": "Zusammenführen",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        2260,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Deterministische Kalkulation – Beispielwerte, in der Praxis aus Tabelle 'preismatrix'\nconst fs = $('Factsheet erstellen').first().json.output;\nlet r = {};\ntry { r = JSON.parse(String($('Recherche-Agent').first().json.output).replace(/```json|```/g, '')); } catch (e) {}\n\nconst P = { projektmanagement: 2500, kosten_minute: 1.25, kosten_kontakt: 3,\n            gruppendiskussion: 3800, tabellenband: 1500, bericht: 3000 };\n\nconst inzidenz = Math.max(fs.inzidenz_laut_kunde ?? r.inzidenz ?? 0.5, 0.02);\nconst n = fs.fallzahl || 0;\nconst loi = fs.interviewdauer_min || 20;\nconst proComplete = loi * P.kosten_minute + P.kosten_kontakt * (1 / inzidenz - 1);\n\nconst posten = [\n  { posten: 'Projektmanagement', betrag: P.projektmanagement },\n  n ? { posten: `Feldarbeit CAPI (n=${n}, ${loi} Min., Inzidenz ${Math.round(inzidenz * 100)} %)`, betrag: Math.round(n * proComplete) } : null,\n  fs.anzahl_gruppendiskussionen ? { posten: `${fs.anzahl_gruppendiskussionen} Gruppendiskussionen`, betrag: fs.anzahl_gruppendiskussionen * P.gruppendiskussion } : null,\n  (fs.deliverables || []).join(' ').toLowerCase().includes('tabell') ? { posten: 'Tabellenband', betrag: P.tabellenband } : null,\n  (fs.deliverables || []).join(' ').toLowerCase().includes('bericht') ? { posten: 'Ergebnisbericht', betrag: P.bericht } : null,\n].filter(Boolean);\n\nconst netto = posten.reduce((s, p) => s + p.betrag, 0);\nreturn [{ json: { posten, netto, inzidenz, recherche: r, kapazitaet: $('Ampel bilden').first().json } }];"
      },
      "id": "0e63b879-56df-43d7-9db3-1ce3e81b8d76",
      "name": "Kalkulation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2480,
        300
      ]
    },
    {
      "parameters": {
        "operation": "sendAndWait",
        "sendTo": "clientservice@institut.example",
        "subject": "=Angebot prüfen: {{ $('Factsheet erstellen').item.json.output.zielgruppe }} – {{ $json.netto.toLocaleString('de-DE') }} € netto",
        "message": "=FACTSHEET\n{{ JSON.stringify($('Factsheet erstellen').item.json.output, null, 2) }}\n\nANNAHMEN\nInzidenz: {{ Math.round($json.inzidenz*100) }} % (Sicherheit: {{ $json.recherche.sicherheit || 'unbekannt' }})\nQuellen: {{ ($json.recherche.quellen || []).join(', ') }}\n\nKAPAZITÄT: {{ $json.kapazitaet.ampel.toUpperCase() }}\n\nKALKULATION\n{{ $json.posten.map(p => `${p.posten}: ${p.betrag.toLocaleString('de-DE')} €`).join('\\n') }}\nSumme netto: {{ $json.netto.toLocaleString('de-DE') }} €",
        "responseType": "customForm",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Entscheidung",
              "fieldType": "dropdown",
              "requiredField": true,
              "fieldOptions": {
                "values": [
                  {
                    "option": "Freigeben"
                  },
                  {
                    "option": "Korrigieren"
                  },
                  {
                    "option": "Ablehnen"
                  }
                ]
              }
            },
            {
              "fieldLabel": "Korrigierter Preis netto (EUR)",
              "fieldType": "number"
            },
            {
              "fieldLabel": "Kommentar",
              "fieldType": "textarea"
            }
          ]
        },
        "options": {}
      },
      "id": "c9a7d7c5-1b99-45c2-9b08-0c7a270f1bba",
      "name": "Freigabe Client Service",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2700,
        300
      ]
    },
    {
      "parameters": {
        "mode": "expression",
        "numberOutputs": 3,
        "output": "={{ ({ 'Freigeben': 0, 'Korrigieren': 1, 'Ablehnen': 2 })[$json.data['Entscheidung']] ?? 2 }}"
      },
      "id": "e79fb94d-22bb-493a-b789-0f1c62c254e9",
      "name": "Entscheidung",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        2920,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const kalk = $('Kalkulation').first().json;\nconst neu = Number($json.data['Korrigierter Preis netto (EUR)']);\nreturn [{ json: { ...kalk, netto: neu || kalk.netto, korrigiert: !!neu, kommentar: $json.data['Kommentar'] || '' } }];"
      },
      "id": "3587c3ad-5472-4d63-9aa2-c395fc93bf0e",
      "name": "Korrektur übernehmen",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3140,
        420
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE anfragen SET status = 'abgelehnt', kommentar = $2 WHERE id = $1;",
        "options": {
          "queryReplacement": "={{ $('Anfrage speichern').item.json.id }},{{ $json.data['Kommentar'] || '' }}"
        }
      },
      "id": "31008495-1c5d-4262-94b4-90ed7ed5054a",
      "name": "Als abgelehnt ablegen",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        3140,
        580
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://anonymax.local:8080/api/v1/reidentify",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ anfrage_id: $('Anfrage speichern').item.json.id, felder: ['kunde_platzhalter', 'ansprechpartner', 'email'] }) }}",
        "options": {}
      },
      "id": "c94c727f-4757-4715-93ce-bf571ff06d5f",
      "name": "Anonymax: Kundendaten einsetzen",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3360,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const kunde = $json;  // von Anonymax wieder eingesetzt, nur lokal\nconst fs = $('Factsheet erstellen').first().json.output;\nconst k = $('Kalkulation').first().json;\nconst korr = $('Korrektur übernehmen').isExecuted ? $('Korrektur übernehmen').first().json : null;\nconst netto = korr ? korr.netto : k.netto;\nconst eur = v => v.toLocaleString('de-DE') + ' €';\n\nconst html = `<!doctype html><html lang=\"de\"><meta charset=\"utf-8\"><style>\nbody{font-family:Georgia,serif;margin:48px;color:#222} h1{font-weight:400}\ntable{width:100%;border-collapse:collapse} td{padding:6px 0;border-bottom:1px solid #ddd}\ntd:last-child{text-align:right} .sum td{font-weight:700;border-top:2px solid #222}\n</style><body>\n<p>${kunde.firma}<br>${kunde.ansprechpartner}</p>\n<h1>Angebot: ${fs.zielgruppe}</h1>\n<p>Methode: ${fs.methode} · Fallzahl: ${fs.fallzahl || '–'} · Ergebnislieferung: ${fs.deadline}</p>\n<table>${k.posten.map(p => `<tr><td>${p.posten}</td><td>${eur(p.betrag)}</td></tr>`).join('')}\n<tr class=\"sum\"><td>Summe netto${korr && korr.korrigiert ? ' (angepasst)' : ''}</td><td>${eur(netto)}</td></tr></table>\n<p>Annahme Inzidenz: ${Math.round(k.inzidenz * 100)} %. Abweichungen werden vor Feldstart abgestimmt.</p>\n</body></html>`;\n\nreturn [{ json: { html, netto, email: kunde.email, betreff: `Ihr Angebot: ${fs.zielgruppe}` } }];"
      },
      "id": "7961624b-1030-4c21-9eed-a7f3ac47a79b",
      "name": "Angebot gestalten",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3580,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://gotenberg:3000/forms/chromium/convert/html",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formData",
              "name": "files",
              "value": "={{ $json.html }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "angebot"
            }
          }
        }
      },
      "id": "86f24644-6890-4f85-83a6-ad027f18c0ed",
      "name": "PDF rendern (Gotenberg)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3800,
        300
      ]
    },
    {
      "parameters": {
        "resource": "draft",
        "operation": "create",
        "subject": "={{ $('Angebot gestalten').item.json.betreff }}",
        "emailType": "text",
        "message": "=Guten Tag,\n\nanbei erhalten Sie unser Angebot. Für Rückfragen stehe ich gern zur Verfügung.\n\nMit freundlichen Grüßen",
        "options": {
          "sendTo": "={{ $('Angebot gestalten').item.json.email }}",
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "angebot"
              }
            ]
          }
        }
      },
      "id": "98c4d124-d4aa-4afe-8533-49ce3ebe34ef",
      "name": "Mail-Entwurf anlegen",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        4020,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE anfragen SET status = 'angebot_entwurf', preis_netto = $2 WHERE id = $1;\nINSERT INTO angebote (anfrage_id, preis_netto, ki_preis_netto, erstellt) VALUES ($1, $2, $3, now());",
        "options": {
          "queryReplacement": "={{ $('Anfrage speichern').item.json.id }},{{ $('Angebot gestalten').item.json.netto }},{{ $('Kalkulation').item.json.netto }}"
        }
      },
      "id": "38f404d1-d3e9-463c-87d5-f8090af31812",
      "name": "Angebot ablegen",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        4240,
        300
      ]
    },
    {
      "parameters": {
        "amount": 7,
        "unit": "days"
      },
      "id": "1cce5ad2-cfa8-4100-b064-fb1c377f23ab",
      "name": "7 Tage warten",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        4460,
        300
      ]
    },
    {
      "parameters": {
        "sendTo": "clientservice@institut.example",
        "subject": "=Nachfassen: {{ $('Angebot gestalten').item.json.betreff }}",
        "emailType": "text",
        "message": "=Das Angebot ist seit 7 Tagen draußen. Kurz nachfassen?",
        "options": {}
      },
      "id": "945d35d6-6b54-42f9-a2a6-3831862e8317",
      "name": "Nachfass-Erinnerung",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        4680,
        300
      ]
    }
  ],
  "connections": {
    "Anfrage-Postfach": {
      "main": [
        [
          {
            "node": "Anhang auslesen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anhang auslesen": {
      "main": [
        [
          {
            "node": "Anonymax: maskieren",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anonymax: maskieren": {
      "main": [
        [
          {
            "node": "Factsheet erstellen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Factsheet erstellen": {
      "main": [
        [
          {
            "node": "Anfrage speichern",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ressourcenbedarf ableiten": {
      "main": [
        [
          {
            "node": "Ressourcenkalender prüfen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ressourcenkalender prüfen": {
      "main": [
        [
          {
            "node": "Ampel bilden",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ampel bilden": {
      "main": [
        [
          {
            "node": "Engpass?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kalkulation": {
      "main": [
        [
          {
            "node": "Freigabe Client Service",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Freigabe Client Service": {
      "main": [
        [
          {
            "node": "Entscheidung",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Korrektur übernehmen": {
      "main": [
        [
          {
            "node": "Anonymax: Kundendaten einsetzen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anonymax: Kundendaten einsetzen": {
      "main": [
        [
          {
            "node": "Angebot gestalten",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Angebot gestalten": {
      "main": [
        [
          {
            "node": "PDF rendern (Gotenberg)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PDF rendern (Gotenberg)": {
      "main": [
        [
          {
            "node": "Mail-Entwurf anlegen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mail-Entwurf anlegen": {
      "main": [
        [
          {
            "node": "Angebot ablegen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Angebot ablegen": {
      "main": [
        [
          {
            "node": "7 Tage warten",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7 Tage warten": {
      "main": [
        [
          {
            "node": "Nachfass-Erinnerung",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anfrage speichern": {
      "main": [
        [
          {
            "node": "Recherche-Agent",
            "type": "main",
            "index": 0
          },
          {
            "node": "Ressourcenbedarf ableiten",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recherche-Agent": {
      "main": [
        [
          {
            "node": "Zusammenführen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Engpass?": {
      "main": [
        [
          {
            "node": "Machbarkeit anfragen",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Zusammenführen",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Machbarkeit anfragen": {
      "main": [
        [
          {
            "node": "Zusammenführen",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Zusammenführen": {
      "main": [
        [
          {
            "node": "Kalkulation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Entscheidung": {
      "main": [
        [
          {
            "node": "Anonymax: Kundendaten einsetzen",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Korrektur übernehmen",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Als abgelehnt ablegen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude (Factsheet)": {
      "ai_languageModel": [
        [
          {
            "node": "Factsheet erstellen",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Factsheet-Schema": {
      "ai_outputParser": [
        [
          {
            "node": "Factsheet erstellen",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Claude (Recherche)": {
      "ai_languageModel": [
        [
          {
            "node": "Recherche-Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/Berlin"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}