{
  "name": "Bayer PH - Workflow 2",
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://adb-8164385335211286.6.azuredatabricks.net/api/2.0/sql/statements",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Keep-Alive",
              "value": "timeout=600"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"on_wait_timeout\": \"CANCEL\",\n  \"statement\": \"SELECT *, element_at(filter(PAYMENT_DATA, p -> p.BLOCKED = true AND to_date(p.NET_DUE_DATE) >= current_date() AND to_date(p.NET_DUE_DATE) < date_add(current_date(), 10)), 1).NET_DUE_DATE AS NET_DUE_DATE, element_at(PURCHASE_ORDER_NUMBERS, 1).PURCHASE_ORDER_NUMBER AS PO_NUMBER FROM generaldiscovery_intfc_procurement_r.proc_pi_invoice_status_fosm_intfc WHERE exists(PAYMENT_DATA, p -> p.BLOCKED = true AND to_date(p.NET_DUE_DATE) >= current_date() AND to_date(p.NET_DUE_DATE) < date_add(current_date(), 10)) ORDER BY NET_DUE_DATE ASC LIMIT 10;\",\n  \"wait_timeout\": \"50s\",\n  \"warehouse_id\": \"638730ddd6aa4c47\"\n}",
        "options": {
          "timeout": 55000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        320,
        -64
      ],
      "id": "e466230d-9f7a-40fd-853f-20848adb39a7",
      "name": "Fetch DBX",
      "credentials": {
        "httpBearerAuth": {
          "id": "ENI2fMKkg6U9IpZU",
          "name": "QA - Databricks"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// Databricks SQL Result → Structured JSON/YAML Converter\n// ============================================================\n\nconst raw = $input.item.json;\n\n// outputFormat: read from the item if present, otherwise default to 'json'\n// To make this dynamic, add an \"outputFormat\" field in the node before this one\n// const outputFormat = (raw.outputFormat ?? 'json').toLowerCase();\nconst OUTPUT_FORMAT = 'json';\n\n// Fields where null/empty is meaningful and must NOT be stripped.\n// Add to this list if your business logic relies on detecting absence.\nconst KEEP_NULL_FIELDS = new Set([\n  'GR_QUANTITY_TOTAL_ERP',\n  'IR_QUANTITY_TOTAL_ERP',\n  'ORIGINATING_SYSTEM_SB',\n  'PURCHASING_TOOL',\n]);\n\n// ── helpers ──────────────────────────────────────────────────\n\nfunction tryParseJson(value) {\n  if (typeof value !== 'string') return value;\n  const t = value.trim();\n  if (t.startsWith('[') || t.startsWith('{')) {\n    try { return JSON.parse(t); } catch (_) {}\n  }\n  return value;\n}\n\nfunction parseStructFields(typeText) {\n  const m = typeText.match(/STRUCT<(.+)>$/is);\n  if (!m) return null;\n  const inner = m[1];\n  const fields = [];\n  let depth = 0, current = '';\n  for (const ch of inner) {\n    if      (ch === '<')               { depth++; current += ch; }\n    else if (ch === '>')               { depth--; current += ch; }\n    else if (ch === ',' && depth === 0){ fields.push(current.trim()); current = ''; }\n    else                               { current += ch; }\n  }\n  if (current.trim()) fields.push(current.trim());\n  return fields.map(f => {\n    const nm = f.match(/^(\\w+)\\s*:/);\n    return nm ? nm[1] : f.split(':')[0].trim();\n  });\n}\n\nfunction castValue(value, column) {\n  if (value === null || value === undefined) return null;\n  const typeName = (column.type_name || '').toUpperCase();\n\n  if (typeName === 'ARRAY') {\n    const structFields = parseStructFields(column.type_text || '');\n    const parsed = tryParseJson(value);\n    if (Array.isArray(parsed)) {\n      if (structFields) {\n        return parsed.map(item => {\n          if (Array.isArray(item)) {\n            const obj = {};\n            structFields.forEach((f, i) => { obj[f] = item[i] !== undefined ? item[i] : null; });\n            return obj;\n          }\n          if (typeof item === 'object' && item !== null) return item;\n          return item;\n        });\n      }\n      return parsed;\n    }\n    return parsed;\n  }\n\n  if (['LONG','INT','INTEGER','BIGINT','SHORT','BYTE'].includes(typeName)) {\n    const n = Number(value); return isNaN(n) ? value : n;\n  }\n  if (['DECIMAL','FLOAT','DOUBLE'].includes(typeName)) {\n    const n = parseFloat(value); return isNaN(n) ? value : n;\n  }\n  if (typeName === 'BOOLEAN') {\n    if (value === 'true'  || value === true)  return true;\n    if (value === 'false' || value === false) return false;\n    return value;\n  }\n  return value;\n}\n\n// ── null / empty stripper ────────────────────────────────────\n// Removes fields where the value is null, undefined, empty string,\n// empty array, or empty object - UNLESS the key is in KEEP_NULL_FIELDS.\n\nfunction isEmpty(value) {\n  if (value === null || value === undefined) return true;\n  if (value === '') return true;\n  if (Array.isArray(value) && value.length === 0) return true;\n  if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) return true;\n  return false;\n}\n\nfunction stripEmpty(value, keyName) {\n  // Preserve meaningful nulls on protected fields\n  if (keyName && KEEP_NULL_FIELDS.has(keyName)) return value;\n\n  if (Array.isArray(value)) {\n    const cleaned = value\n      .map(v => stripEmpty(v, null))\n      .filter(v => !isEmpty(v));\n    return cleaned;\n  }\n\n  if (value !== null && typeof value === 'object') {\n    const out = {};\n    for (const [k, v] of Object.entries(value)) {\n      const cleaned = stripEmpty(v, k);\n      if (KEEP_NULL_FIELDS.has(k) || !isEmpty(cleaned)) {\n        out[k] = cleaned;\n      }\n    }\n    return out;\n  }\n\n  return value;\n}\n\n// ── YAML serialiser ───────────────────────────────────────────\n\nfunction yamlScalar(value, indent) {\n  if (value === null || value === undefined) return 'null';\n  if (typeof value === 'boolean') return String(value);\n  if (typeof value === 'number')  return String(value);\n\n  const s = String(value);\n  const needsQuote =\n    s === '' ||\n    s === 'null' || s === 'true' || s === 'false' ||\n    /^[\\s]|[\\s]$/.test(s) ||\n    /[:#\\[\\]{},&*?|<>=!%@`\"']/.test(s) ||\n    /^\\d/.test(s) ||\n    s.includes('\\n');\n\n  if (!needsQuote) return s;\n\n  if (s.includes('\\n')) {\n    const pad = '  '.repeat(indent + 1);\n    return '|-\\n' + s.split('\\n').map(l => pad + l).join('\\n');\n  }\n\n  return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"') + '\"';\n}\n\nfunction yamlValue(value, indent) {\n  if (value === null || value === undefined) return 'null';\n  if (typeof value !== 'object') return yamlScalar(value, indent);\n\n  if (Array.isArray(value)) {\n    if (value.length === 0) return '[]';\n    const pad = '  '.repeat(indent);\n    return value.map(item => {\n      if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n        const keys = Object.keys(item);\n        if (keys.length === 0) return `\\n${pad}- {}`;\n        return keys.map((k, i) => {\n          const v = item[k];\n          const valStr = (v !== null && typeof v === 'object')\n            ? yamlValue(v, indent + 2)\n            : ' ' + yamlScalar(v, indent + 1);\n          const prefix = i === 0 ? `\\n${pad}- ` : `\\n${pad}  `;\n          return `${prefix}${k}:${typeof v === 'object' && v !== null && yamlValue(v, indent+2).startsWith('\\n') ? yamlValue(v, indent+2) : valStr}`;\n        }).join('');\n      }\n      return `\\n${pad}- ${yamlValue(item, indent + 1)}`;\n    }).join('');\n  }\n\n  // Mapping\n  const keys = Object.keys(value);\n  if (keys.length === 0) return '{}';\n  const pad = '  '.repeat(indent);\n  return keys.map(k => {\n    const v = value[k];\n    if (v !== null && typeof v === 'object') {\n      const inner = yamlValue(v, indent + 1);\n      return `\\n${pad}${k}:${inner.startsWith('\\n') ? inner : ' ' + inner}`;\n    }\n    return `\\n${pad}${k}: ${yamlScalar(v, indent)}`;\n  }).join('');\n}\n\nfunction toYaml(records) {\n  if (!Array.isArray(records) || records.length === 0) return 'result: []\\n';\n\n  // ── Top-level wrapper: \"result:\" with indented list items ──\n  const lines = ['result:'];\n\n  for (const record of records) {\n    const keys = Object.keys(record);\n    if (keys.length === 0) { lines.push('  - {}'); continue; }\n\n    keys.forEach((k, i) => {\n      const v = record[k];\n      const prefix = i === 0 ? '  - ' : '    ';\n\n      if (v !== null && typeof v === 'object') {\n        const inner = yamlValue(v, 3);\n        // inner starts with \\n for block style\n        lines.push(`${prefix}${k}:${inner.startsWith('\\n') ? inner : ' ' + inner}`);\n      } else {\n        lines.push(`${prefix}${k}: ${yamlScalar(v, 2)}`);\n      }\n    });\n  }\n\n  return lines.join('\\n') + '\\n';\n}\n\n// ── Main transformation ───────────────────────────────────────\n\nconst columns   = raw?.manifest?.schema?.columns ?? [];\nconst dataArray = raw?.result?.data_array ?? [];\n\nconst sortedColumns = [...columns].sort((a, b) => a.position - b.position);\n\nconst rawRecords = dataArray.map(row => {\n  const record = {};\n  for (const col of sortedColumns) {\n    record[col.name] = castValue(row[col.position], col);\n  }\n  return record;\n});\n\n// Strip nulls / empty values (preserving protected fields)\nconst records = rawRecords.map(r => stripEmpty(r, null));\n\n// ── Output ────────────────────────────────────────────────────\n\nif (OUTPUT_FORMAT === 'yaml') {\n  return {\n    json: {\n      output: toYaml(records),\n      format: 'yaml',\n      rowCount: records.length\n    }\n  };\n} else {\n  return {\n    json: {\n      output: {\n        result: records\n      },\n      format: 'json',\n      rowCount: records.length\n    }\n  };\n}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        544,
        -64
      ],
      "id": "c80beb6f-37cf-4faf-90b1-8e0e3306f244",
      "name": "Parse response to json or yaml"
    },
    {
      "parameters": {
        "jsCode": "const toRecipients = String($('Recipients').first().json.to || '')\n  .split(/[;,]/).map(s => s.trim()).filter(Boolean)\n  .map(address => ({ emailAddress: { address } }));\n\nconst ccRecipients = String($('Recipients').first().json.cc || '')\n  .split(/[;,]/).map(s => s.trim()).filter(Boolean)\n  .map(address => ({ emailAddress: { address } }));\n\nconst res = $input.first().json;\nlet rows = (res.output && res.output.result) ? res.output.result\n         : (Array.isArray(res.result) ? res.result : []);\n\nconst LOGO_B64 = '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';\n\nconst today = new Date(); today.setHours(0,0,0,0);\nconst MAX_ROWS = 25;\nconst sym = { EUR:'€', USD:'$', GBP:'£', SEK:'kr ', NOK:'kr ', DKK:'kr ', CHF:'CHF ', PLN:'zł ' };\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\nconst money = (a, c) => {\n  if (a === null || a === undefined || a === '') return '—';\n  const n = Number(a); if (isNaN(n)) return String(a);\n  return (sym[c] || (c ? c + ' ' : '')) + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n};\nconst dateUK = (d) => {\n  if (!d) return '—';\n  const dt = new Date(d); if (isNaN(dt)) return String(d);\n  return dt.toLocaleDateString('en-GB', { day:'2-digit', month:'short', year:'numeric' });\n};\nconst dueDays = (d) => {\n  if (!d) return null;\n  const dt = new Date(d); if (isNaN(dt)) return null;\n  dt.setHours(0,0,0,0);\n  return Math.round((dt - today) / 86400000);\n};\nconst fmtN = (x) => x.toLocaleString('en-US');\n\nconst logoHtml = `<img src=\"cid:bayerlogo\" width=\"64\" height=\"64\" alt=\"Bayer\" style=\"display:block; border:0; outline:none;\">`;\n\nrows.sort((a, b) => (new Date(a.NET_DUE_DATE || 0)) - (new Date(b.NET_DUE_DATE || 0)));\nconst total = rows.length;\nlet overdue = 0;\nconst sums = {};\nrows.forEach(r => {\n  const d = dueDays(r.NET_DUE_DATE);\n  if (d !== null && d < 0) overdue++;\n  const c = r.DOCUMENT_CURRENCY || '';\n  const n = Number(r.INVOICE_TOTAL_AMOUNT_DC);\n  if (!isNaN(n)) sums[c] = (sums[c] || 0) + n;\n});\nconst curList = Object.entries(sums).sort((a, b) => b[1] - a[1]);\nlet totalMain = '—', totalSub = '';\nif (curList.length) {\n  totalMain = money(curList[0][1], curList[0][0]);\n  if (curList.length > 1) totalSub = 'across ' + curList.length + ' currencies';\n}\n\nconst shown = rows.slice(0, MAX_ROWS);\nconst rowHtml = shown.map(r => {\n  const ref    = r.REFERENCE_NUMBER || '—';\n  const vendor = r.VENDOR_NAME || 'Unknown vendor';\n  const po     = r.PO_NUMBER\n              || (Array.isArray(r.PURCHASE_ORDER_NUMBERS) && r.PURCHASE_ORDER_NUMBERS[0] && r.PURCHASE_ORDER_NUMBERS[0].PURCHASE_ORDER_NUMBER)\n              || '—';\n  const dueRaw = r.NET_DUE_DATE || null;\n  const days = dueDays(dueRaw);\n  let accent='#00BCFF', bg='#e6f4fb', txt='#00617f', label='due';\n  if (days !== null) {\n    if (days < 0)       { accent='#d92d20'; bg='#fdecea'; txt='#b42318'; label = Math.abs(days) + ' days overdue'; }\n    else if (days <= 3) { accent='#f79009'; bg='#fff4e5'; txt='#b54708'; label = days === 0 ? 'due today' : 'due in ' + days + ' days'; }\n    else                { label = 'due in ' + days + ' days'; }\n  }\n  const descHtml = r.description\n    ? `<div style=\"font-size:12px; color:#5f6571; margin-top:6px; line-height:1.5;\">${esc(r.description)}</div>`\n    : '';\n  return `\n  <tr><td colspan=\"3\" style=\"border-top:1px solid #edf0f3; font-size:0; line-height:0;\">&nbsp;</td></tr>\n  <tr>\n    <td valign=\"top\" style=\"padding:13px 8px 13px 12px; border-left:3px solid ${accent};\">\n      <div style=\"font-size:14px; color:#10384F; font-weight:bold;\">${esc(vendor)}</div>\n      <div style=\"font-size:12px; color:#8a909a; margin-top:3px;\">REF ${esc(ref)} &middot; PO ${esc(po)}</div>\n      ${descHtml}\n    </td>\n    <td valign=\"top\" style=\"padding:13px 8px; white-space:nowrap;\">\n      <span style=\"background:${bg}; color:${txt}; font-size:11px; font-weight:bold; padding:4px 9px; border-radius:11px;\">${label}</span>\n      <div style=\"font-size:12px; color:#8a909a; margin-top:5px;\">${dateUK(dueRaw)}</div>\n    </td>\n    <td valign=\"top\" align=\"right\" style=\"padding:13px 0 13px 8px; font-size:14px; color:#10384F; font-weight:bold; white-space:nowrap;\">${money(r.INVOICE_TOTAL_AMOUNT_DC, r.DOCUMENT_CURRENCY || '')}</td>\n  </tr>`;\n}).join('');\n\nconst empty = `<tr><td colspan=\"3\" style=\"padding:24px 0; text-align:center; color:#8a909a; font-size:14px;\">No invoices are currently due.</td></tr>`;\nconst reportDate = today.toLocaleDateString('en-GB', { day:'2-digit', month:'long', year:'numeric' });\n\nconst html = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"></head>\n<body style=\"margin:0; padding:0; background:#eef0f3;\">\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#eef0f3;\"><tr><td align=\"center\" style=\"padding:22px 12px;\">\n<table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px; max-width:600px; font-family:Arial,Helvetica,sans-serif; background:#ffffff; border:1px solid #e6e8eb; border-radius:14px; overflow:hidden;\">\n  <tr><td style=\"padding:0;\">\n    <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse:collapse;\">\n      <tr style=\"font-size:0; line-height:0;\"><td width=\"50%\" style=\"background:#89D329; height:6px;\">&nbsp;</td><td width=\"50%\" style=\"background:#00BCFF; height:6px;\">&nbsp;</td></tr>\n    </table>\n  </td></tr>\n  <tr><td style=\"background:#ffffff; padding:24px 28px 0;\">\n    ${logoHtml}\n    <div style=\"color:#10384F; font-size:21px; font-weight:bold; margin-top:14px;\">Invoices due for your attention</div>\n    <div style=\"color:#6b7280; font-size:13px; margin-top:4px;\">As of ${reportDate} &middot; ${fmtN(total)} invoices need action</div>\n    <div style=\"border-top:3px solid #66B512; width:46px; margin-top:14px; font-size:0; line-height:0;\">&nbsp;</div>\n  </td></tr>\n  <tr><td style=\"background:#ffffff; padding:18px 28px 24px;\">\n    <div style=\"color:#1a1a1a; font-size:15px;\">Hi there,</div>\n    <div style=\"color:#4b5563; font-size:14px; line-height:1.6; margin-top:8px;\">The following invoices linked to your account are due. Overdue items are flagged in red.</div>\n    <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-top:20px;\"><tr>\n      <td width=\"33%\" valign=\"top\" style=\"background:#eef7e0; border-radius:10px; padding:14px;\"><div style=\"color:#4a7a10; font-size:12px;\">Invoices due</div><div style=\"color:#4a7a10; font-size:24px; font-weight:bold; margin-top:3px;\">${fmtN(total)}</div></td>\n      <td width=\"10\">&nbsp;</td>\n      <td width=\"33%\" valign=\"top\" style=\"background:#fdecea; border-radius:10px; padding:14px;\"><div style=\"color:#b42318; font-size:12px;\">Overdue</div><div style=\"color:#b42318; font-size:24px; font-weight:bold; margin-top:3px;\">${fmtN(overdue)}</div></td>\n      <td width=\"10\">&nbsp;</td>\n      <td width=\"33%\" valign=\"top\" style=\"background:#e6f4fb; border-radius:10px; padding:14px;\"><div style=\"color:#00617f; font-size:12px;\">Total due</div><div style=\"color:#10384F; font-size:20px; font-weight:bold; margin-top:3px;\">${totalMain}</div>${totalSub ? `<div style=\"color:#00617f; font-size:11px; margin-top:3px;\">${totalSub}</div>` : ''}</td>\n    </tr></table>\n    <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-top:24px; border-collapse:collapse;\">\n      <tr>\n        <td style=\"font-size:11px; color:#9aa1ab; text-transform:uppercase; letter-spacing:.5px; padding:0 8px 10px 12px;\">Vendor / Invoice</td>\n        <td style=\"font-size:11px; color:#9aa1ab; text-transform:uppercase; letter-spacing:.5px; padding:0 8px 10px; white-space:nowrap;\">Due date</td>\n        <td align=\"right\" style=\"font-size:11px; color:#9aa1ab; text-transform:uppercase; letter-spacing:.5px; padding:0 0 10px 8px;\">Amount</td>\n      </tr>\n      ${rowHtml || empty}\n      <tr><td colspan=\"3\" style=\"border-top:1px solid #edf0f3; font-size:0; line-height:0;\">&nbsp;</td></tr>\n    </table>\n  </td></tr>\n  <tr><td style=\"background:#f7f8fa; padding:16px 28px; border-top:1px solid #edf0f3;\">\n    <div style=\"color:#10384F; font-size:12px; font-weight:bold; letter-spacing:2px;\">BAYER</div>\n    <div style=\"color:#9aa3af; font-size:11px; line-height:1.6; margin-top:4px;\">Automated summary from Microflow. Amounts shown in document currency. Reply to this email or contact procurement support with questions.</div>\n  </td></tr>\n</table>\n</td></tr></table>\n</body></html>`;\n\nconst subject = `${fmtN(total)} invoice(s) due — ${fmtN(overdue)} overdue`;\n\nconst payload = {\n  message: {\n    subject,\n    body: { contentType: 'HTML', content: html },\n    toRecipients,\n    ccRecipients,\n    attachments: [{\n      '@odata.type': '#microsoft.graph.fileAttachment',\n      name: 'bayer.png',\n      contentType: 'image/png',\n      isInline: true,\n      contentId: 'bayerlogo',\n      contentBytes: LOGO_B64\n    }]\n  },\n  saveToSentItems: true\n};\n\nreturn [{ json: { payload, subject, total_count: total, overdue_count: overdue } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1568,
        -64
      ],
      "id": "1ccf0103-6bc5-4fb5-bc89-6a01e0fa9c59",
      "name": "Build HTML Email"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f85ccc52-f310-498e-a119-9b47e58581d4",
              "name": "to",
              "value": "aemal.sayer@avanai.io",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        96,
        -64
      ],
      "id": "a2271ae9-456a-4d16-9f87-8021ab5ad84a",
      "name": "Recipients"
    },
    {
      "parameters": {
        "fieldToSplitOut": "output.result",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        768,
        -64
      ],
      "id": "7b7adc33-d413-4f51-8771-6ea46548eb01",
      "name": "Split Out"
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n  \"REFERENCE_NUMBER\": \"5500001551\",\n  \"VENDOR_NAME\": \"Bayer Ariba SCC DE1\",\n  \"PO_NUMBER\": \"2170076015\",\n  \"NET_DUE_DATE\": \"2026-06-19\",\n  \"INVOICE_TOTAL_AMOUNT_DC\": 4641,\n  \"DOCUMENT_CURRENCY\": \"EUR\",\n  \"description\": \"Invoice from Bayer Ariba SCC DE1 for 4,641 EUR due 19 Jun 2026, blocked for price/quantity mismatch on PO 2170076015.\"\n}"
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        1184,
        160
      ],
      "id": "fb640a59-4240-455d-9504-ddf2fa0c0e0c",
      "name": "Structured Output Parser"
    },
    {
      "parameters": {
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "output",
              "renameField": true,
              "outputFieldName": "result"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1344,
        -64
      ],
      "id": "b82c571e-4ffc-44ff-9c5e-77b488565d35",
      "name": "Aggregate"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        944,
        160
      ],
      "id": "faf5c41f-46cb-4cac-b5fd-0ebab58ba2d7",
      "name": "MGA",
      "credentials": {
        "openAiApi": {
          "id": "8VD37B3VjsV90xtg",
          "name": "MGA - Bayer"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -128,
        -64
      ],
      "id": "09d210d0-59ee-447b-ac30-df8439c32ba9",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Invoice data (JSON):\n{{ JSON.stringify($json) }}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are a procurement analyst assistant at Bayer. You receive the COMPLETE JSON record for one supplier invoice. Return a JSON object with these fields:\n- REFERENCE_NUMBER, VENDOR_NAME, NET_DUE_DATE, INVOICE_TOTAL_AMOUNT_DC, DOCUMENT_CURRENCY: copy EXACTLY from the input. Never change numbers or dates.\n- PO_NUMBER: use PO_NUMBER, else PURCHASE_ORDER_NUMBERS[0].PURCHASE_ORDER_NUMBER.\n- description: EXACTLY ONE sentence, max 30 words, factual analysis for an approver (vendor, amount+currency, due/overdue, payment block/reason, PO, notable flags). Use only provided data. No markdown.\nOutput only the JSON object."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.7,
      "position": [
        992,
        -64
      ],
      "id": "4c10bf14-bbfc-42f9-89fc-2c3125ad87fe",
      "name": "AI Agent",
      "retryOnFail": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://graph.microsoft.com/v1.0/me/sendMail",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftOutlookOAuth2Api",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.payload) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1792,
        -64
      ],
      "id": "30a39438-b4f3-4d19-aca5-a34ea1f8eb57",
      "name": "Send Email",
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "id": "EaO0sPk3aeT30PvG",
          "name": "Microsoft Outlook account FLOW"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Fetch DBX": {
      "main": [
        [
          {
            "node": "Parse response to json or yaml",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse response to json or yaml": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Email": {
      "main": [
        [
          {
            "node": "Send Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recipients": {
      "main": [
        [
          {
            "node": "Fetch DBX",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Build HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MGA": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Recipients",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "2592310b-cc7e-443c-96df-68be329a1d0b",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "001309b9a164202f2985c1d8d866889efcbea70a119463cc5254d24cb015b3cd"
  },
  "nodeGroups": [],
  "id": "grDvlTF1m0MOqUhp",
  "tags": []
}