{
  "id": "o7Cj6FMfaXx3li9E",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Supplier Consolidation Recommendation Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "23210f3a-8214-4b2f-9793-a219d361c998",
      "name": "Supplier Analysis Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -448,
        -96
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "7f314f03-00b0-4e70-8c78-09fc4b89e716",
      "name": "Fetch Supplier Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -240,
        -96
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1sRjC7JdVLukXBJTJXfz-4Pa8cLxOWwIkcRxQxaw5sfo/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1sRjC7JdVLukXBJTJXfz-4Pa8cLxOWwIkcRxQxaw5sfo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1sRjC7JdVLukXBJTJXfz-4Pa8cLxOWwIkcRxQxaw5sfo/edit?usp=drivesdk",
          "cachedResultName": "supplier records"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "0b463ce5-8531-4d6f-b05b-f3e19bccc82a",
      "name": "Clean & Structure Supplier Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -32,
        -96
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "7b192d94-2a9d-4b5b-b623-0494116a4d8d",
              "name": "Name",
              "type": "string",
              "value": "={{ $json.Name }}"
            },
            {
              "id": "6abe4c89-a4c3-4cae-a587-d24d4f96aeff",
              "name": "Department",
              "type": "string",
              "value": "={{ $json.Department }}"
            },
            {
              "id": "51ca714e-b6b0-4a60-84a7-94a6b5b4279d",
              "name": "Category",
              "type": "string",
              "value": "={{ $json.Category }}"
            },
            {
              "id": "8038adc3-fb66-47c2-bf70-4919d9a42296",
              "name": "Monthly_Spend",
              "type": "number",
              "value": "={{ $json.Monthly_Spend }}"
            },
            {
              "id": "1c1c59e5-8ede-4eda-8753-84f74fd95bbe",
              "name": "Risk_Rating",
              "type": "string",
              "value": "={{ $json.Risk_Rating }}"
            },
            {
              "id": "770b532b-da54-4a77-8afd-ce1388b04e76",
              "name": "Service_Criticality",
              "type": "string",
              "value": "={{ $json.Service_Criticality }}"
            },
            {
              "id": "8bd49380-56ae-41bf-86c9-ad9217924309",
              "name": "Backup_Supplier_Available",
              "type": "string",
              "value": "={{ $json.Backup_Supplier_Available }}"
            },
            {
              "id": "261a43f8-05f3-4dde-bcbc-d7dc9b7adc9c",
              "name": "Email",
              "type": "string",
              "value": "={{ $json.Email }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "cfbd85ac-60c1-472a-8403-bd679ca156b5",
      "name": "Identify Overlaps & Calculate Savings",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        -96
      ],
      "parameters": {
        "jsCode": "function normalize(name) {\n  if (!name) return '';\n\n  return name\n    .toLowerCase()\n    .replace(/\\b(pvt ltd|ltd|inc|technologies|tech)\\b/g, '')\n    .replace(/[^\\w\\s]/g, '')\n    .trim();\n}\n\nconst grouped = {};\n\nfor (const item of items) {\n  const rawName = item.json.Name || '';\n  const normalizedName = normalize(rawName);\n  const department = item.json.Department || 'Unknown';\n  const spend = Number(item.json.Monthly_Spend || 0);\n  const email = item.json.Email || '';\n\n  if (!normalizedName) continue;\n\n  if (!grouped[normalizedName]) {\n    grouped[normalizedName] = {\n      supplier: rawName,\n      departments: new Set(),\n      emails: new Set(),\n      total_spend: 0\n    };\n  }\n\n  grouped[normalizedName].departments.add(department);\n\n  if (email) {\n    grouped[normalizedName].emails.add(email);\n  }\n\n  grouped[normalizedName].total_spend += spend;\n}\n\nconst result = [];\n\nfor (const key in grouped) {\n  const data = grouped[key];\n  const departmentCount = data.departments.size;\n  const totalSpend = data.total_spend;\n\n  if (departmentCount > 1) {\n    let savingsPercent = 0;\n\n    // Dynamic savings logic\n    if (departmentCount >= 4 && totalSpend > 100000) {\n      savingsPercent = 12;\n    } else if (departmentCount >= 3 && totalSpend > 50000) {\n      savingsPercent = 10;\n    } else if (departmentCount >= 3) {\n      savingsPercent = 8;\n    } else if (departmentCount === 2 && totalSpend > 30000) {\n      savingsPercent = 6;\n    } else if (departmentCount === 2) {\n      savingsPercent = 4;\n    } else {\n      savingsPercent = 2;\n    }\n\n    const savingsAmount = (totalSpend * savingsPercent) / 100;\n\n    let opportunityLevel = 'Low';\n    if (savingsPercent >= 10) {\n      opportunityLevel = 'High';\n    } else if (savingsPercent >= 5) {\n      opportunityLevel = 'Medium';\n    }\n\n    result.push({\n      json: {\n        supplier: data.supplier,\n        departments: [...data.departments].join(', '),\n        email: [...data.emails].join(', '),\n        department_count: departmentCount,\n        total_spend: totalSpend,\n        savings_percent: savingsPercent,\n        savings_amount: savingsAmount,\n        opportunity_level: opportunityLevel\n      }\n    });\n  }\n}\n\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "c984b28b-7e02-4dff-84b5-d143282f5430",
      "name": "Evaluate Risk vs Savings",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        416,
        96
      ],
      "parameters": {
        "text": "=You are a procurement expert.\n\nAnalyze this supplier consolidation opportunity:\n\nSupplier: {{$json.supplier}}\nDepartments: {{$json.departments}}\nDepartment Count: {{$json.department_count}}\nTotal Spend: {{$json.total_spend}}\nSavings Percent: {{$json.savings_percent}}\nSavings Amount: {{$json.savings_amount}}\nOpportunity Level: {{$json.opportunity_level}}\n\nReturn ONLY raw JSON.\nDo not add markdown.\nDo not use code fences.\nDo not add any explanation before or after JSON.\n\nUse exactly this structure:\n{\n  \"risk\": \"Low or Medium or High\",\n  \"decision\": \"Recommended or Not Recommended\",\n  \"recommendation\": \"short recommendation\",\n  \"reason\": \"short explanation\"\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "c7091f4c-c1c2-4336-869a-dbc08e0a5576",
      "name": "Extract AI Insights",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        96
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const raw = item.json.output || '{}';\n\n  let parsed = {};\n\n  try {\n    parsed = JSON.parse(raw);\n  } catch (error) {\n    parsed = {\n      risk: 'Unknown',\n      decision: 'Error',\n      recommendation: 'Parsing failed',\n      reason: 'The AI response was not valid JSON'\n    };\n  }\n\n  return {\n    json: {\n      ...item.json,\n      risk: parsed.risk,\n      decision: parsed.decision,\n      recommendation: parsed.recommendation,\n      reason: parsed.reason\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "b89186dd-037c-4eaa-907c-9bfd0362c7d6",
      "name": "Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        400,
        288
      ],
      "parameters": {
        "model": "openai/gpt-oss-120b",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d7884d4b-da1e-4b1f-8612-b58df584045d",
      "name": "Combine Supplier & AI Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        976,
        -112
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "b6c0ed69-a69b-4bcf-b48d-d2c09d3d2463",
      "name": "Prepare Final Recommendation",
      "type": "n8n-nodes-base.set",
      "position": [
        1184,
        -112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "5eeb525f-560a-4997-8717-e2a51fea569c",
              "name": "Supplier",
              "type": "string",
              "value": "={{ $json.supplier }}"
            },
            {
              "id": "51e7253b-d5cf-4e9e-8584-05b88c24ed5d",
              "name": "departments",
              "type": "string",
              "value": "={{ $json.departments }}"
            },
            {
              "id": "57878f30-984b-4351-9a7d-2c582948d6a7",
              "name": "department_count",
              "type": "number",
              "value": "={{ $json.department_count }}"
            },
            {
              "id": "88d3b5b6-3a32-475a-a156-4c3530c80280",
              "name": "total_spend",
              "type": "number",
              "value": "={{ $json.total_spend }}"
            },
            {
              "id": "8e772d39-b286-4e9f-a03f-f303bd4461af",
              "name": "savings_percent",
              "type": "number",
              "value": "={{ $json.savings_percent }}"
            },
            {
              "id": "ac1a69b9-b5d2-416e-84a5-d18cf38e378d",
              "name": "savings_amount",
              "type": "number",
              "value": "={{ $json.savings_amount }}"
            },
            {
              "id": "fb18dbfb-15cc-4b39-b0c3-ce4749dcbfe8",
              "name": "risk",
              "type": "string",
              "value": "={{ $json.risk }}"
            },
            {
              "id": "0f8f15c0-ac91-4226-b4ea-b9f187b47508",
              "name": "decision",
              "type": "string",
              "value": "={{ $json.decision }}"
            },
            {
              "id": "044686a4-4220-44f6-b810-8ec322e21f81",
              "name": "recommendation",
              "type": "string",
              "value": "={{ $json.recommendation }}"
            },
            {
              "id": "6b455ff4-3784-4cd8-9968-53f98057dc98",
              "name": "reason",
              "type": "string",
              "value": "={{ $json.reason }}"
            },
            {
              "id": "5f3d80fb-7504-4531-bc9c-825c1018cd86",
              "name": "Email",
              "type": "string",
              "value": "={{$json.email}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ba3a8d47-cc77-48e6-8bfc-72812be74366",
      "name": "Send Slack Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        1472,
        -192
      ],
      "parameters": {
        "text": "=Supplier Consolidation Recommendation  \nSupplier: {{$json.Supplier}} \nDepartments: {{ $json.departments }} \nDepartment Count: {{ $json.department_count }} \nTotal Spend:  {{ $json.total_spend }}\nSavings Percent: {{ $json.savings_percent }}\nSavings Amount:  {{ $json.savings_amount }}\nRisk: {{ $json.risk }}\nDecision:{{ $json.decision }}\nRecommendation: {{ $json.recommendation }}\nReason: {{ $json.reason }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0ATE2C0ZTN",
          "cachedResultName": "supplier-insights"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "69785bcd-cb7c-4c1c-83ed-fa10b0ef8052",
      "name": "Send Email Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1472,
        16
      ],
      "parameters": {
        "sendTo": "={{ $json.Email }}",
        "message": "=<div style=\"font-family: Arial, sans-serif; line-height: 1.6; color: #222;\">\n  <h2 style=\"margin-bottom: 10px;\">Supplier Consolidation Recommendation</h2>\n\n  <p><strong>Supplier:</strong> {{$json.Supplier}}</p>\n  <p><strong>Departments:</strong> {{$json.departments}}</p>\n  <p><strong>Department Count:</strong> {{$json.department_count}}</p>\n\n  <hr style=\"border: none; border-top: 1px solid #ddd; margin: 16px 0;\">\n\n  <p><strong>Total Spend:</strong> {{$json.total_spend}}</p>\n  <p><strong>Savings Percent:</strong> {{$json.savings_percent}}%</p>\n  <p><strong>Savings Amount:</strong> {{$json.savings_amount}}</p>\n\n  <hr style=\"border: none; border-top: 1px solid #ddd; margin: 16px 0;\">\n\n  <p><strong>Risk Level:</strong> {{$json.risk}}</p>\n  <p><strong>Decision:</strong> {{$json.decision}}</p>\n\n  <p><strong>Recommendation:</strong><br>{{$json.recommendation}}</p>\n\n  <p><strong>Reason:</strong><br>{{$json.reason}}</p>\n\n  <br>\n\n  <p>Regards,<br>Procurement Automation System</p>\n</div>",
        "options": {
          "appendAttribution": false
        },
        "subject": "=Supplier Consolidation Recommendation - {{$json.Supplier}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "342c2e14-4477-415d-a0a4-eb0b7dee8542",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -368
      ],
      "parameters": {
        "width": 384,
        "height": 800,
        "content": "# Supplier Consolidation Recommendation \n\n# How it works:\n\nThis workflow identifies overlapping suppliers, estimates savings, and uses AI to evaluate risk versus savings before generating recommendations. Final insights are shared via email and Slack for quick decision-making.\n\n# Setup Steps:\n1. Scheduled Trigger\nRun workflow weekly.\n\n2. Data Collection\nFetch supplier data from Google Sheets.\n\n3. Overlap & Savings Detection\nIdentify shared suppliers and calculate savings.\n\n4.  AI Analysis\nEvaluate risk vs savings.\n\n5.  Data Processing\nCombine and format results.\n\n6. Output & Notification\nSend email and Slack alerts.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f6629d33-19c6-412e-b444-78760658b58b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 416,
        "content": "## Supplier Data Collection & Preparation\n\nTrigger the workflow on schedule, fetch supplier records from Google Sheets, and clean/structure key fields like department, spend, and email to prepare data for analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "0f71abc3-a9bf-4912-af1d-7bfeb642e1ba",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 688,
        "content": "\n## Supplier Analysis & AI Insights\n\nIdentify overlapping suppliers across departments and calculate potential savings. Use AI to evaluate risk versus savings and extract structured insights for decision-making."
      },
      "typeVersion": 1
    },
    {
      "id": "2f12fc66-4376-4f10-9225-070412ccf133",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 816,
        "height": 672,
        "content": "## Final Recommendation & Notifications\nCombine supplier and AI data, format the final recommendation, and send insights to stakeholders via Slack alerts and dynamic email reports for quick action."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "006398fa-b4ba-441f-afa7-58c8f2fd27ed",
  "connections": {
    "Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Evaluate Risk vs Savings",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract AI Insights": {
      "main": [
        [
          {
            "node": "Combine Supplier & AI Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Fetch Supplier Data": {
      "main": [
        [
          {
            "node": "Clean & Structure Supplier Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Risk vs Savings": {
      "main": [
        [
          {
            "node": "Extract AI Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supplier Analysis Trigger": {
      "main": [
        [
          {
            "node": "Fetch Supplier Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Supplier & AI Data": {
      "main": [
        [
          {
            "node": "Prepare Final Recommendation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Final Recommendation": {
      "main": [
        [
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean & Structure Supplier Data": {
      "main": [
        [
          {
            "node": "Identify Overlaps & Calculate Savings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify Overlaps & Calculate Savings": {
      "main": [
        [
          {
            "node": "Evaluate Risk vs Savings",
            "type": "main",
            "index": 0
          },
          {
            "node": "Combine Supplier & AI Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}