{
  "id": "K9AODHZ2NFsMzqfl",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "N-0133 Budget vs Spend Variance Monitoring Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "04a5f23d-e537-4b98-9db0-82eb3f571d65",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1456,
        528
      ],
      "parameters": {
        "width": 592,
        "height": 352,
        "content": "## Budget vs Spend Monitoring Workflow\n\n## How it works:\nThis workflow runs daily to compare budget vs actual spend across departments. It calculates variance, classifies risk levels, and uses AI to generate root cause insights. If variance exceeds the threshold, alerts are sent via Slack. All results are logged in Google Sheets, and a daily email report is sent with detailed insights.\n\n## Setup steps:\n1. Connect Google Sheets (budget & spend data)\n2. Configure Gemini API credentials\n3. Set Slack channel for alerts\n4. Configure Gmail credentials for email reporting\n5. Adjust variance thresholds if needed\n6. Activate workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "ccd59f2e-6e63-4ee3-bc8d-7b23c2c65f74",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        1088
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 560,
        "content": "## Data Processing\nFetch and combine budget and spend data.\nThen calculate variance and percentage difference."
      },
      "typeVersion": 1
    },
    {
      "id": "0a62056f-8bf6-4709-b7ad-f8b05aac4422",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 800,
        "height": 464,
        "content": "## AI-Powered Financial Risk Analysis & Alert Decision Engine\n\nAI to analyze budget differences, identify the reason for overspending or underspending, assign a risk level (Low, Medium, High), and suggest cost-saving actions. A decision check then decides if the variance is high enough to trigger an alert."
      },
      "typeVersion": 1
    },
    {
      "id": "299e8a62-cc2e-429e-a5f2-cf65932efe55",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        1088
      ],
      "parameters": {
        "color": 7,
        "width": 1104,
        "height": 528,
        "content": "## Alerting, Reporting & Financial Audit Logging\n\nSends real-time Slack alerts for critical budget deviations\nLogs complete analysis into Google Sheets for audit tracking\nSends structured daily email reports to stakeholders\nThis ensures visibility, traceability, and accountability in budget monitoring."
      },
      "typeVersion": 1
    },
    {
      "id": "9c1efc47-3e07-4bb0-8678-46dbfab2c944",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1456,
        1232
      ],
      "parameters": {
        "color": 7,
        "height": 288,
        "content": "## Scheduler\nruns automatically every day at 9:00 AM"
      },
      "typeVersion": 1
    },
    {
      "id": "d020e834-e68f-44ff-a269-c49ad239de3a",
      "name": "Send Daily Budget Email Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1472,
        1360
      ],
      "parameters": {
        "message": "=<p>Hello,</p>\n\n<p>Here is today's budget monitoring report:</p>\n\n<p><b>Date:</b> {{ $json['Run Date'] }}</p>\n\n<p>\n<b>Department:</b> {{$json.Department}}<br>\n<b>Budget:</b> {{$json.Budget}}<br>\n<b>Actual Spend:</b> {{$json.Spend}}\n</p>\n\n<p>\n<b>Variance:</b> {{ $json.Variance }} ({{ $json['Variance %'] }}%)<br>\n<b>Status:</b> {{ $('Prepare Final Report Data').item.json.status_label }}\n</p>\n\n<p><b>AI Insights:</b></p>\n<ul>\n  <li><b>Root Cause:</b> {{ $json['Root Cause'] }}</li>\n  <li><b>Risk Level:</b> {{ $json['Risk Level'] }}</li>\n  <li><b>Recommendation:</b> {{ $json.Recommendation }}</li>\n</ul>\n\n<p><b>Alert Sent:</b> {{ $json['Alert Sent'] }}</p>\n\n<p>Regards,<br>\nBudget Monitoring System</p>",
        "options": {},
        "subject": "=Daily Budget Report - {{ $json['Run Date'] }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ac2ee4d8-7fcf-4718-9762-a3c2ff104c73",
      "name": "Daily Budget Monitoring Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1392,
        1360
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "26d47017-81ad-4e68-bb11-8cb8c0b0b501",
      "name": "Retrieve Budget Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1120,
        1264
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A/edit#gid=0",
          "cachedResultName": "Budget Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A",
          "cachedResultName": "Budget data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8867a23a-5f4d-4bec-a904-92ffeb443c1e",
      "name": "Retrieve Actual Spend Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1120,
        1456
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1909585946,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A/edit#gid=1909585946",
          "cachedResultName": "Actual spend"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A",
          "cachedResultName": "Budget data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c92ca891-eaf0-4e2d-8451-5e2400e0c1d5",
      "name": "Merge Budget and Spend Records",
      "type": "n8n-nodes-base.merge",
      "position": [
        -896,
        1360
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "fieldsToMatchString": "Department"
      },
      "typeVersion": 3.2
    },
    {
      "id": "89477da8-6cc3-48b8-81e0-92bc40870f01",
      "name": "Calculate Budget Variance",
      "type": "n8n-nodes-base.code",
      "position": [
        -672,
        1360
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const budget = parseFloat(item.json.Budget) || 0;\n  const spend = parseFloat(item.json.Spend) || 0;\n  const variance = spend - budget;\n  let variancePercent = 0;\n  if (budget !== 0) {\n    variancePercent = parseFloat(((variance / budget) * 100).toFixed(2));\n  }\n  return {\n    json: {\n      ...item.json,\n      variance: parseFloat(variance.toFixed(2)),\n      variancePercent\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "8b8114f2-86ff-480e-a38f-9379bb19e193",
      "name": "Add Status & Context Details",
      "type": "n8n-nodes-base.set",
      "position": [
        -448,
        1360
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "set-processed-at",
              "name": "processed_at",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            },
            {
              "id": "set-status-label",
              "name": "status_label",
              "type": "string",
              "value": "={{ Math.abs($json.variancePercent) > 20 ? 'Critical' : Math.abs($json.variancePercent) > 10 ? 'Warning' : 'Healthy' }}"
            },
            {
              "id": "set-over-under",
              "name": "over_under",
              "type": "string",
              "value": "={{ $json.variance > 0 ? 'Over budget' : 'Under budget' }}"
            },
            {
              "id": "set-summary",
              "name": "summary",
              "type": "string",
              "value": "={{ $json.Department }} is {{ $json.variance > 0 ? 'over' : 'under' }} budget by {{ Math.abs($json.variancePercent) }}% ({{ $json.variance > 0 ? '+' : '' }}{{ $json.variance }})"
            },
            {
              "id": "set-run-date",
              "name": "run_date",
              "type": "string",
              "value": "={{ new Date().toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' }) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "23e325a2-d440-4b57-8e6e-833013936221",
      "name": "AI-Based Budget Analysis",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        -224,
        1360
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Analyze this procurement variance and return ONLY a raw JSON object \u2014 no markdown, no code fences, no explanation. Just the JSON.\n\nDepartment: {{ $('Calculate Budget Variance').item.json.Department }}\nBudget: {{ $('Calculate Budget Variance').item.json.Budget }}\nSpend: {{ $('Calculate Budget Variance').item.json.Spend }}\nVariance: {{ $('Calculate Budget Variance').item.json.variance }}\nVariance %:{{ $('Calculate Budget Variance').item.json.variancePercent }}\nStatus: {{$json[\"status_label\"]}}\nSummary: {{$json[\"summary\"]}}\n\nRespond with exactly this structure:\n{\"root_cause\": \"...\", \"risk_level\": \"Low|Medium|High\", \"recommendation\": \"...\"}"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "retryOnFail": false,
      "typeVersion": 1.1,
      "alwaysOutputData": false
    },
    {
      "id": "b1197ef8-ac51-4d25-b565-582d9d666b4a",
      "name": "Extract AI Insights",
      "type": "n8n-nodes-base.code",
      "position": [
        128,
        1360
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  let rawText = '';\n\n  try {\n    const parts = item.json?.content?.parts;\n    if (Array.isArray(parts) && parts.length > 0) {\n      rawText = parts[0].text || '';\n    } else if (typeof item.json?.text === 'string') {\n      rawText = item.json.text;\n    } else if (typeof item.json?.content === 'string') {\n      rawText = item.json.content;\n    }\n  } catch (e) {\n    rawText = '';\n  }\n\n  rawText = rawText.trim();\n  rawText = rawText.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/, '').trim();\n\n  let parsed = { root_cause: 'N/A', risk_level: 'Unknown', recommendation: 'N/A' };\n  try {\n    const candidate = JSON.parse(rawText);\n    parsed.root_cause     = candidate.root_cause     || 'N/A';\n    parsed.risk_level     = candidate.risk_level     || 'Unknown';\n    parsed.recommendation = candidate.recommendation || 'N/A';\n  } catch (e) {\n    parsed.root_cause = rawText || 'Parse error \u2014 check Gemini response';\n  }\n\n  return {\n    json: {\n      ...item.json,\n      root_cause:     parsed.root_cause,\n      risk_level:     parsed.risk_level,\n      recommendation: parsed.recommendation\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "8b37c46a-f870-426d-8527-100e2d564b40",
      "name": "Check Variance Alert Condition",
      "type": "n8n-nodes-base.if",
      "position": [
        352,
        1360
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9cf869da-be4f-48ac-98dc-a7b8bdc86a48",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $('Calculate Budget Variance').item.json.variancePercent }}",
              "rightValue": 10
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "03082d60-8e44-4fb9-a540-e600be4af821",
      "name": "Send Budget Alert to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        624,
        1264
      ],
      "parameters": {
        "text": "=:warning: *Budget Alert!*\n\n*Department:* {{ $('Retrieve Actual Spend Data').item.json.Department }}\n*Status:* {{ $('Add Status & Context Details').item.json.status_label }}\n*Run Date:* {{ $('Add Status & Context Details').item.json.run_date }}\n*Budget:* {{ $('Retrieve Budget Data').item.json.Budget }}\n*Actual Spend:* {{ $('Retrieve Actual Spend Data').item.json.Spend }}\n*Variance:* {{ $('Calculate Budget Variance').item.json.variance }} ({{ $('Calculate Budget Variance').item.json.variancePercent }}%)\n*Over/Under:* {{ $('Add Status & Context Details').item.json.over_under }}\n\n*AI Root Cause:* {{ $json.root_cause }}\n*Risk Level:* {{ $json.risk_level }}\n*Recommendation:* {{ $json.recommendation }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0AP6S28KM3",
          "cachedResultName": "all-aishvarya"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "caf27eb4-d2ef-4288-a21a-e202bc4c9b54",
      "name": "Mark Alert as Sent",
      "type": "n8n-nodes-base.set",
      "position": [
        800,
        1264
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "7c4e6b89-475d-4aff-9077-55ec43357a6e",
              "name": "alert_sent",
              "type": "string",
              "value": "true"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f2e35bf0-9d65-4f3d-82d7-3f556fa1d3ce",
      "name": "Set No-Alert Status",
      "type": "n8n-nodes-base.set",
      "position": [
        800,
        1456
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "set-no-alert-flag",
              "name": "alert_sent",
              "type": "string",
              "value": "false"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b20e1de8-2c74-4e8d-b499-57c381521729",
      "name": "Prepare Final Report Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1024,
        1360
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "stamp-alert-sent",
              "name": "alert_sent",
              "type": "string",
              "value": "={{ $json.alert_sent }}"
            },
            {
              "id": "stamp-processed-at",
              "name": "processed_at",
              "type": "string",
              "value": "={{ $('Add Status & Context Details').item.json.processed_at }}"
            },
            {
              "id": "stamp-status-label",
              "name": "status_label",
              "type": "string",
              "value": "={{ $('Add Status & Context Details').item.json.status_label }}"
            },
            {
              "id": "stamp-run-date",
              "name": "run_date",
              "type": "string",
              "value": "={{ $('Add Status & Context Details').item.json.run_date }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "fca70a1d-1ec4-4393-83c2-e47d9257c8d5",
      "name": "Store Final Report in Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1248,
        1360
      ],
      "parameters": {
        "columns": {
          "value": {
            "Spend": "={{ $('Calculate Budget Variance').item.json.Spend }}",
            "Budget": "={{ $('Calculate Budget Variance').item.json.Budget }}",
            "Run Date": "={{ $json.run_date }}",
            "Variance": "={{ $('Calculate Budget Variance').item.json.variance }}",
            "Alert Sent": "={{ $json.alert_sent }}",
            "Department": "={{ $('Calculate Budget Variance').item.json.Department }}",
            "Risk Level": "={{ $('Extract AI Insights').item.json.risk_level }}",
            "Root Cause": "={{ $('Extract AI Insights').item.json.root_cause }}",
            "Variance %": "={{ $('Calculate Budget Variance').item.json.variancePercent }}",
            "Processed At": "={{ $json.processed_at }}",
            "Recommendation": "={{ $('Extract AI Insights').item.json.recommendation }}"
          },
          "schema": [
            {
              "id": "Department",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Department",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Budget",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Budget",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Spend",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Spend",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Variance",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Variance",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Variance %",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Variance %",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Root Cause",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Root Cause",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Risk Level",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Risk Level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Recommendation",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Recommendation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Alert Sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Alert Sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Processed At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Processed At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Run Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Run Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1468998019,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A/edit#gid=1468998019",
          "cachedResultName": "Final report log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1qdCtazd9qFhwO13fuEBbvb7ALDnS0YADToN7hvqs_1A",
          "cachedResultName": "Budget data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "3adbefca-e5bd-48a6-9883-bac28f4c18d9",
  "connections": {
    "Mark Alert as Sent": {
      "main": [
        [
          {
            "node": "Prepare Final Report Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract AI Insights": {
      "main": [
        [
          {
            "node": "Check Variance Alert Condition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set No-Alert Status": {
      "main": [
        [
          {
            "node": "Prepare Final Report Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Budget Data": {
      "main": [
        [
          {
            "node": "Merge Budget and Spend Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI-Based Budget Analysis": {
      "main": [
        [
          {
            "node": "Extract AI Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Budget Variance": {
      "main": [
        [
          {
            "node": "Add Status & Context Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Final Report Data": {
      "main": [
        [
          {
            "node": "Store Final Report in Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Actual Spend Data": {
      "main": [
        [
          {
            "node": "Merge Budget and Spend Records",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Send Budget Alert to Slack": {
      "main": [
        [
          {
            "node": "Mark Alert as Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Status & Context Details": {
      "main": [
        [
          {
            "node": "AI-Based Budget Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Variance Alert Condition": {
      "main": [
        [
          {
            "node": "Send Budget Alert to Slack",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set No-Alert Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Budget and Spend Records": {
      "main": [
        [
          {
            "node": "Calculate Budget Variance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Budget Monitoring Trigger": {
      "main": [
        [
          {
            "node": "Retrieve Budget Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Retrieve Actual Spend Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Final Report in Google Sheets": {
      "main": [
        [
          {
            "node": "Send Daily Budget Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}