{
  "nodes": [
    {
      "id": "ae624b0a-0d31-441c-a32f-64849ef4dcf1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        288
      ],
      "parameters": {
        "color": 3,
        "width": 440,
        "height": 676,
        "content": "### Send weekly invoice summary with overdue alerts from Google Sheets\n\nIf you track invoices in a spreadsheet but nobody checks it until cash flow gets tight, this gives you an automatic weekly report with the numbers that actually matter. It also pings Slack when invoices are overdue so nothing slips through.\n\n### How it works\n1. Runs every Monday at 9 AM (you can change the schedule)\n2. Reads all rows from your invoice tracker in Google Sheets\n3. A code node crunches the numbers: total paid, total unpaid, total overdue. It also builds an HTML email with a proper breakdown table.\n4. The summary report gets emailed to whoever you configure\n5. If anything is overdue, a Slack message goes out with the details so your team can chase it\n\n### Setup\n1. Create a Google Sheet with an \"Invoices\" tab. Columns: Invoice ID, Client Name, Amount, Due Date, Status (paid/unpaid/overdue)\n2. Open \"Configure Settings\" and add your Sheet ID, recipient email, Slack channel ID, and business name\n3. Connect your Google Sheets, Gmail, and Slack credentials\n4. Activate and wait for Monday morning (or trigger it manually to test)"
      },
      "typeVersion": 1
    },
    {
      "id": "83283740-b51e-4ab5-a0fb-7407b5d1792d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        384
      ],
      "parameters": {
        "height": 140,
        "content": "## 1. Weekly trigger\nFires every Monday at 9 AM. Edit the schedule if you want it daily or biweekly."
      },
      "typeVersion": 1
    },
    {
      "id": "9161ddb9-6f51-41d7-94d3-b45eae72658a",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        704
      ],
      "parameters": {
        "width": 260,
        "height": 140,
        "content": "## 2. Your settings\nSheet ID, recipient email, Slack channel, business name. Change these and you're good to go."
      },
      "typeVersion": 1
    },
    {
      "id": "1b548090-3d80-477e-9627-dd271b3469f4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        368
      ],
      "parameters": {
        "width": 280,
        "height": 140,
        "content": "## 3. Pull the data\nGrabs every row from your invoice spreadsheet."
      },
      "typeVersion": 1
    },
    {
      "id": "c12fcbb2-3f81-4c8e-96c7-19e1eff60036",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        736
      ],
      "parameters": {
        "width": 300,
        "height": 140,
        "content": "## 4. Crunch the numbers\nAdds up paid, unpaid, and overdue totals. Builds the email HTML with a table of overdue invoices and how many days late each one is."
      },
      "typeVersion": 1
    },
    {
      "id": "79add24c-176c-442b-b251-884e594022bb",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2240,
        320
      ],
      "parameters": {
        "width": 412,
        "height": 140,
        "content": "## 5. Email report + Slack alert\nThe full summary gets emailed. If any invoices are overdue, Slack gets a separate alert with the specifics."
      },
      "typeVersion": 1
    },
    {
      "id": "d02ca829-e017-4b1e-b130-3454322a0793",
      "name": "Every Monday 9 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1152,
        544
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e06c3942-d62e-4101-bee7-f9b5a8340d88",
      "name": "Configure Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        1424,
        544
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "config-1",
              "name": "googleSheetId",
              "type": "string",
              "value": "YOUR_GOOGLE_SHEET_ID"
            },
            {
              "id": "config-2",
              "name": "sheetName",
              "type": "string",
              "value": "Invoices"
            },
            {
              "id": "config-3",
              "name": "recipientEmail",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "config-4",
              "name": "slackChannel",
              "type": "string",
              "value": "YOUR_SLACK_CHANNEL_ID"
            },
            {
              "id": "config-5",
              "name": "businessName",
              "type": "string",
              "value": "Your Business Name"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0bd76ab3-5fed-4d5f-b768-723cbb0c50db",
      "name": "Read All Invoices",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1728,
        544
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.sheetName }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.googleSheetId }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "996b70f4-8cf3-439d-9a0c-ef9147fe991e",
      "name": "Calculate Totals and Find Overdue",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        544
      ],
      "parameters": {
        "jsCode": "const invoices = $input.all().map(i => i.json);\nconst config = $('Configure Settings').first().json;\nconst today = new Date();\n\nlet totalPaid = 0;\nlet totalUnpaid = 0;\nlet totalOverdue = 0;\nconst overdueList = [];\nconst unpaidList = [];\n\nfor (const inv of invoices) {\n  const amount = parseFloat(inv['Amount'] || inv['amount'] || 0);\n  const status = (inv['Status'] || inv['status'] || '').toLowerCase();\n  const dueDate = new Date(inv['Due Date'] || inv['due_date'] || '');\n  const clientName = inv['Client Name'] || inv['client_name'] || 'Unknown';\n  const invoiceId = inv['Invoice ID'] || inv['invoice_id'] || 'N/A';\n\n  if (status === 'paid') {\n    totalPaid += amount;\n  } else {\n    totalUnpaid += amount;\n    unpaidList.push({ invoiceId, clientName, amount, dueDate: dueDate.toISOString().split('T')[0] });\n\n    if (dueDate < today) {\n      totalOverdue += amount;\n      const daysOverdue = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));\n      overdueList.push({ invoiceId, clientName, amount, dueDate: dueDate.toISOString().split('T')[0], daysOverdue });\n    }\n  }\n}\n\nconst overdueRows = overdueList.map(o =>\n  `<tr><td>${o.invoiceId}</td><td>${o.clientName}</td><td>$${o.amount.toFixed(2)}</td><td>${o.dueDate}</td><td style=\"color:red;font-weight:bold;\">${o.daysOverdue} days</td></tr>`\n).join('');\n\nconst unpaidRows = unpaidList.map(u =>\n  `<tr><td>${u.invoiceId}</td><td>${u.clientName}</td><td>$${u.amount.toFixed(2)}</td><td>${u.dueDate}</td></tr>`\n).join('');\n\nconst emailHtml = `\n<h2>Weekly Invoice Summary - ${config.businessName}</h2>\n<p>Report generated: ${today.toISOString().split('T')[0]}</p>\n<table style=\"border-collapse:collapse;width:100%;\">\n  <tr style=\"background:#f0f0f0;\"><td style=\"padding:8px;\"><strong>Total Paid</strong></td><td style=\"padding:8px;\">$${totalPaid.toFixed(2)}</td></tr>\n  <tr><td style=\"padding:8px;\"><strong>Total Unpaid</strong></td><td style=\"padding:8px;\">$${totalUnpaid.toFixed(2)}</td></tr>\n  <tr style=\"background:#fff0f0;\"><td style=\"padding:8px;\"><strong>Total Overdue</strong></td><td style=\"padding:8px;color:red;font-weight:bold;\">$${totalOverdue.toFixed(2)}</td></tr>\n</table>\n${overdueList.length > 0 ? `\n<h3>Overdue Invoices (${overdueList.length})</h3>\n<table style=\"border-collapse:collapse;width:100%;\">\n  <tr style=\"background:#f0f0f0;\"><th style=\"padding:8px;text-align:left;\">ID</th><th style=\"padding:8px;text-align:left;\">Client</th><th style=\"padding:8px;text-align:left;\">Amount</th><th style=\"padding:8px;text-align:left;\">Due Date</th><th style=\"padding:8px;text-align:left;\">Overdue</th></tr>\n  ${overdueRows}\n</table>` : '<p>No overdue invoices this week.</p>'}\n${unpaidList.length > 0 ? `\n<h3>All Unpaid Invoices (${unpaidList.length})</h3>\n<table style=\"border-collapse:collapse;width:100%;\">\n  <tr style=\"background:#f0f0f0;\"><th style=\"padding:8px;text-align:left;\">ID</th><th style=\"padding:8px;text-align:left;\">Client</th><th style=\"padding:8px;text-align:left;\">Amount</th><th style=\"padding:8px;text-align:left;\">Due Date</th></tr>\n  ${unpaidRows}\n</table>` : ''}\n`;\n\nconst slackOverdueText = overdueList.map(o =>\n  `- *${o.invoiceId}* | ${o.clientName} | $${o.amount.toFixed(2)} | Due: ${o.dueDate} (*${o.daysOverdue} days overdue*)`\n).join('\\n');\n\nreturn [{\n  json: {\n    ...config,\n    emailHtml,\n    totalPaid,\n    totalUnpaid,\n    totalOverdue,\n    totalPaidFormatted: totalPaid.toFixed(2),\n    totalUnpaidFormatted: totalUnpaid.toFixed(2),\n    totalOverdueFormatted: totalOverdue.toFixed(2),\n    overdueCount: overdueList.length,\n    hasOverdue: overdueList.length > 0,\n    slackOverdueText,\n    reportDate: today.toISOString().split('T')[0]\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "5a21a430-64d1-4699-9c35-73d6b6046e1a",
      "name": "Email Weekly Summary",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2384,
        512
      ],
      "parameters": {
        "sendTo": "={{ $json.recipientEmail }}",
        "message": "={{ $json.emailHtml }}",
        "options": {},
        "subject": "=Weekly Invoice Summary - {{ $json.businessName }} ({{ $json.reportDate }})"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ab40f58c-20ac-4e3b-a58c-296cf4db84ae",
      "name": "Any Overdue?",
      "type": "n8n-nodes-base.if",
      "position": [
        2384,
        704
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": false
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-overdue",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.hasOverdue }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f336716c-b603-4b83-8535-801e0567b0e6",
      "name": "Alert Overdue to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        2624,
        688
      ],
      "parameters": {
        "text": "=:warning: *Overdue Invoice Alert* - {{ $json.businessName }}\n{{ $json.overdueCount }} invoice(s) overdue, totalling *${{ $json.totalOverdueFormatted }}*\n\n{{ $json.slackOverdueText }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "#general"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.4
    }
  ],
  "connections": {
    "Any Overdue?": {
      "main": [
        [
          {
            "node": "Alert Overdue to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every Monday 9 AM": {
      "main": [
        [
          {
            "node": "Configure Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read All Invoices": {
      "main": [
        [
          {
            "node": "Calculate Totals and Find Overdue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Settings": {
      "main": [
        [
          {
            "node": "Read All Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Totals and Find Overdue": {
      "main": [
        [
          {
            "node": "Email Weekly Summary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Any Overdue?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}