{
  "name": "Insolvenz-Radar: WF3 Event Alerts",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "5 6 * * *"
            }
          ]
        }
      },
      "id": "cron-after-wf1",
      "name": "Cron 06:05 UTC (nach WF1)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT e.id, e.debtor_name_raw, e.court, e.file_number, e.event_date, e.event_type, e.debtor_domicile, e.state, e.entity_type, e.source_url, c.name_canonical AS company_name, c.enrichment_status FROM radar_insolvency_events e LEFT JOIN radar_companies c ON e.canonical_company_id = c.id WHERE e.created_at > NOW() - INTERVAL '15 minutes' AND e.entity_type = 'company' ORDER BY e.event_date DESC",
        "options": {}
      },
      "id": "fetch-recent",
      "name": "Neue Company-Events (letzte 15 Min)",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        220,
        0
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-events",
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "check-events",
      "name": "Events vorhanden?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Filter nach konfigurierbaren Kriterien\n// Anpassen je nach Bedarf:\n\nconst ALERT_EVENT_TYPES = [\n  'er\u00f6ffnung',\n  'anordnung',\n  'vorl\u00e4ufige insolvenzverwaltung',\n  'insolvenzverfahren er\u00f6ffnet',\n];\n\n// Leeres Array = alle Bundesl\u00e4nder\nconst ALERT_STATES = [];\n// Beispiel f\u00fcr NRW + Bayern: ['NW', 'BY']\n\nconst results = [];\n\nfor (const item of $input.all()) {\n  const eventType = (item.json.event_type || '').toLowerCase();\n  const state = item.json.state || '';\n  \n  // Event-Type Filter\n  const typeMatch = ALERT_EVENT_TYPES.length === 0 || \n    ALERT_EVENT_TYPES.some(t => eventType.includes(t));\n  \n  // State Filter  \n  const stateMatch = ALERT_STATES.length === 0 || \n    ALERT_STATES.includes(state);\n  \n  if (typeMatch && stateMatch) {\n    results.push(item);\n  }\n}\n\nreturn results.length > 0 ? results : [{ json: { _empty: true } }];"
      },
      "id": "filter-criteria",
      "name": "Filter (Region/Typ)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "not-empty",
              "leftValue": "={{ $json._empty }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "notTrue"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "check-filtered",
      "name": "Relevante Events?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Formatiere Slack-Nachricht\nconst items = $input.all();\n\nlet blocks = [];\nblocks.push({\n  type: 'header',\n  text: {\n    type: 'plain_text',\n    text: `Insolvenz-Radar: ${items.length} neue Bekanntmachung${items.length > 1 ? 'en' : ''}`,\n  }\n});\n\nblocks.push({ type: 'divider' });\n\nfor (const item of items.slice(0, 10)) {\n  const d = item.json;\n  const fields = [\n    `*${d.debtor_name_raw || d.company_name}*`,\n    `Gericht: ${d.court || '?'}`,\n    `Az: ${d.file_number || '?'}`,\n    `Art: ${d.event_type || '?'}`,\n    `Bundesland: ${d.state || '?'}`,\n    d.source_url ? `<${d.source_url}|Quelle>` : '',\n  ].filter(Boolean);\n  \n  blocks.push({\n    type: 'section',\n    text: {\n      type: 'mrkdwn',\n      text: fields.join(' | '),\n    }\n  });\n}\n\nif (items.length > 10) {\n  blocks.push({\n    type: 'context',\n    elements: [{\n      type: 'mrkdwn',\n      text: `... und ${items.length - 10} weitere`,\n    }]\n  });\n}\n\nreturn [{\n  json: {\n    blocks: JSON.stringify(blocks),\n    text: `Insolvenz-Radar: ${items.length} neue Bekanntmachung${items.length > 1 ? 'en' : ''}`,\n    eventCount: items.length,\n  }\n}];"
      },
      "id": "format-slack",
      "name": "Slack-Nachricht formatieren",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        0
      ]
    },
    {
      "parameters": {
        "channel": "#insolvenz-radar",
        "text": "={{ $json.text }}",
        "blocksUi": "={{ $json.blocks }}",
        "otherOptions": {}
      },
      "id": "slack-alert",
      "name": "Slack Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1320,
        0
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "notes": "Slack Credential: Bot Token mit chat:write. Channel #insolvenz-radar muss existieren."
    },
    {
      "parameters": {},
      "id": "no-op-no-events",
      "name": "Keine Events (Skip)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        660,
        200
      ]
    },
    {
      "parameters": {},
      "id": "no-op-filtered",
      "name": "Keine relevanten (Skip)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1100,
        200
      ]
    }
  ],
  "connections": {
    "Cron 06:05 UTC (nach WF1)": {
      "main": [
        [
          {
            "node": "Neue Company-Events (letzte 15 Min)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Neue Company-Events (letzte 15 Min)": {
      "main": [
        [
          {
            "node": "Events vorhanden?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Events vorhanden?": {
      "main": [
        [
          {
            "node": "Filter (Region/Typ)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Keine Events (Skip)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter (Region/Typ)": {
      "main": [
        [
          {
            "node": "Relevante Events?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Relevante Events?": {
      "main": [
        [
          {
            "node": "Slack-Nachricht formatieren",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Keine relevanten (Skip)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack-Nachricht formatieren": {
      "main": [
        [
          {
            "node": "Slack Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "insolvenz-radar"
    },
    {
      "name": "alerts"
    }
  ],
  "meta": {
    "description": "L\u00e4uft 5 Minuten nach WF1 (Daily Feed Ingest). Pr\u00fcft neue Company-Events und sendet Slack-Alerts.\n\nKonfigurierbare Filter (im Code-Node 'Filter'):\n- ALERT_EVENT_TYPES: Welche Verfahrensarten triggern Alerts\n- ALERT_STATES: Welche Bundesl\u00e4nder (leer = alle)\n\nBen\u00f6tigte Credentials:\n1. Neon PostgreSQL (Connection String)\n2. Slack Bot Token (chat:write Berechtigung)\n\nVoraussetzung: Slack-Channel #insolvenz-radar muss existieren."
  }
}