{
  "id": "1aBo5fHHYkhBxhlc",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "WooCommerce Seasonal Sales Planning & Monitor \u2192 SET Pattern Compare \u2192 Slack",
  "tags": [],
  "nodes": [
    {
      "id": "2d57edf2-67d7-4f89-8742-6dffe9a47f98",
      "name": "Weekly Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -8688,
        304
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3a66a614-414a-4af6-a83b-7d578162d251",
      "name": "Global Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -8464,
        304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "threshold",
              "name": "trend_threshold",
              "type": "number",
              "value": 5
            },
            {
              "id": "webhook",
              "name": "teams_webhook_url",
              "type": "string",
              "value": "https://outlook.office.com/webhook/YOUR_WEBHOOK_HERE"
            },
            {
              "id": "sheet",
              "name": "google_sheet_id",
              "type": "string",
              "value": "YOUR_GOOGLE_SHEET_ID_OR_URL"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c21fb239-2c1c-46ef-aaa1-4ca70ba1b130",
      "name": "Calculate Date Ranges",
      "type": "n8n-nodes-base.code",
      "position": [
        -8240,
        304
      ],
      "parameters": {
        "jsCode": "// Calculate Date Ranges based on current timestamp\nconst now = new Date();\nconst oneWeek = 7 * 24 * 60 * 60 * 1000;\n\nfunction getIsoDate(date) {\n  return date.toISOString();\n}\n\n// 1. Current Week\nconst currentEnd = now;\nconst currentStart = new Date(now.getTime() - oneWeek);\n\n// 2. Last Month (Same week)\nconst lastMonthEnd = new Date(now);\nlastMonthEnd.setMonth(lastMonthEnd.getMonth() - 1);\nconst lastMonthStart = new Date(lastMonthEnd.getTime() - oneWeek);\n\n// 3. Last Quarter (Same week)\nconst lastQEnd = new Date(now);\nlastQEnd.setMonth(lastQEnd.getMonth() - 3);\nconst lastQStart = new Date(lastQEnd.getTime() - oneWeek);\n\n// 4. Last Year (Same week)\nconst lastYearEnd = new Date(now);\nlastYearEnd.setFullYear(lastYearEnd.getFullYear() - 1);\nconst lastYearStart = new Date(lastYearEnd.getTime() - oneWeek);\n\nreturn {\n  current: { after: getIsoDate(currentStart), before: getIsoDate(currentEnd) },\n  lastMonth: { after: getIsoDate(lastMonthStart), before: getIsoDate(lastMonthEnd) },\n  lastQuarter: { after: getIsoDate(lastQStart), before: getIsoDate(lastQEnd) },\n  lastYear: { after: getIsoDate(lastYearStart), before: getIsoDate(lastYearEnd) }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f9d2e906-c458-405b-a715-09fefc14cb30",
      "name": "Fetch Current Week Sales",
      "type": "n8n-nodes-base.wooCommerce",
      "position": [
        -8016,
        304
      ],
      "parameters": {
        "options": {
          "after": "={{ $('Calculate Date Ranges').first().json.current.after }}",
          "before": "={{ $('Calculate Date Ranges').first().json.current.before }}",
          "status": "completed"
        },
        "resource": "order",
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "wooCommerceApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8bce7222-548e-4748-8dfb-a9a170f91882",
      "name": "Fetch Last Month Sales",
      "type": "n8n-nodes-base.wooCommerce",
      "position": [
        -7792,
        304
      ],
      "parameters": {
        "options": {
          "after": "={{ $('Calculate Date Ranges').first().json.lastMonth.after }}",
          "before": "={{ $('Calculate Date Ranges').first().json.lastMonth.before }}"
        },
        "resource": "order",
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "wooCommerceApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dd6340b6-4845-4ecc-8e75-28e9e08d593d",
      "name": "Fetch Last Year Sales",
      "type": "n8n-nodes-base.wooCommerce",
      "position": [
        -7568,
        304
      ],
      "parameters": {
        "options": {
          "after": "={{ $('Calculate Date Ranges').first().json.lastYear.after }}",
          "before": "={{ $('Calculate Date Ranges').first().json.lastYear.before }}"
        },
        "resource": "order",
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "wooCommerceApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "4c7e968c-6af2-4198-a6d7-5154e2beda42",
      "name": "Generate Trend Report",
      "type": "n8n-nodes-base.code",
      "position": [
        -7344,
        304
      ],
      "parameters": {
        "jsCode": "// --- Input Retrieval ---\n// We retrieve the Threshold from the Global Config node\nconst threshold = $('Global Configuration').first().json.trend_threshold || 5;\n\n// Retrieve sales data safely (default to empty array if no data found)\nconst currentOrders = $('Fetch Current Week Sales').all().map(i => i.json) || [];\nconst lastMonthOrders = $('Fetch Last Month Sales').all().map(i => i.json) || [];\nconst lastYearOrders = $('Fetch Last Year Sales').all().map(i => i.json) || [];\n\n// --- Helper Functions ---\n\n// 1. Calculate Revenue, Count and SKU tally\nfunction calculateMetrics(orders) {\n  if (!orders || orders.length === 0) {\n    return { revenue: 0, orders: 0, aov: 0, skus: {} };\n  }\n  \n  let revenue = 0;\n  let skuCounts = {};\n  \n  orders.forEach(order => {\n    const orderTotal = parseFloat(order.total) || 0;\n    revenue += orderTotal;\n\n    if (order.line_items && Array.isArray(order.line_items)) {\n      order.line_items.forEach(item => {\n        skuCounts[item.name] = (skuCounts[item.name] || 0) + (item.quantity || 1);\n      });\n    }\n  });\n\n  return {\n    revenue: revenue,\n    orders: orders.length,\n    aov: orders.length > 0 ? (revenue / orders.length) : 0,\n    skus: skuCounts\n  };\n}\n\n// 2. Identify Top SKU\nfunction getTopSku(skuMap) {\n  if (!skuMap || Object.keys(skuMap).length === 0) return 'None';\n  let sorted = Object.entries(skuMap).sort((a,b) => b[1] - a[1]);\n  return sorted.length > 0 ? `${sorted[0][0]} (${sorted[0][1]})` : 'None';\n}\n\n// 3. Determine Trend (No Emojis)\nfunction calcTrend(current, historical) {\n  if (!historical || historical === 0) {\n    if (current === 0) return { percent: '0%', trend: 'Flat' };\n    return { percent: 'N/A', trend: 'New' };\n  }\n\n  const diff = ((current - historical) / historical) * 100;\n  \n  let trend = 'Flat';\n  if (diff > threshold) trend = 'Increase';\n  if (diff < -threshold) trend = 'Decrease';\n  \n  return { percent: diff.toFixed(1) + '%', trend: trend };\n}\n\n// --- Main Execution ---\n\nconst currStats = calculateMetrics(currentOrders);\nconst lmStats = calculateMetrics(lastMonthOrders);\nconst lyStats = calculateMetrics(lastYearOrders);\n\nreturn {\n  revenue: {\n    current: currStats.revenue.toFixed(2),\n    vsMonth: calcTrend(currStats.revenue, lmStats.revenue),\n    vsYear: calcTrend(currStats.revenue, lyStats.revenue)\n  },\n  orders: {\n    current: currStats.orders,\n    vsMonth: calcTrend(currStats.orders, lmStats.orders),\n    vsYear: calcTrend(currStats.orders, lyStats.orders)\n  },\n  topSku: getTopSku(currStats.skus)\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c6e73f3d-7738-4880-a05b-192e731838a3",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -7120,
        400
      ],
      "parameters": {
        "options": {},
        "operation": "append",
        "sheetName": "Sheet1",
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "={{ $('Global Configuration').first().json.google_sheet_id }}"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "702e3546-bad1-45ea-8122-a46f04f38dcd",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -9216,
        -112
      ],
      "parameters": {
        "width": 435,
        "height": 581,
        "content": "## WooCommerce Seasonality Tracker\n\n**How it works**\nThis workflow automates weekly sales reporting by comparing current performance against historical data. It triggers every Monday, calculates date ranges and fetches order data from WooCommerce for the current week, last month and last year. It then aggregates revenue and orders, identifies the top-selling SKU and determines trends (Increase, Decrease, Flat). The final report is sent to Slack and logged to Google Sheets for auditing.\n\n**Setup Steps**\n1. Open the **Global Configuration** node to set your `trend_threshold` (e.g., 5%), `slack_webhook_url` and `google_sheet_id`.\n2. Open each **Fetch Sales** node and select your WooCommerce credentials.\n3. Authenticate the **Log to Google Sheets** node with your Google account.\n4. Activate the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "4f0cc06d-cd5c-4437-9705-8ac91bc647b7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -8736,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 643,
        "height": 410,
        "content": "## 1. Initialization\n\n**Trigger & Config**\nSets the weekly schedule and defines global variables (Webhooks, Thresholds). \n**Date Calculation**\nDynamically generates ISO date ranges for Current, Last Month and Last Year periods."
      },
      "typeVersion": 1
    },
    {
      "id": "7754ac20-fa29-437e-82a6-ba9327caf068",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -8080,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 410,
        "content": "## 2. Data Ingestion\n\n**Sequential Fetching**\nRetrives order data one period at a time (Current -> Month -> Year). This sequential chaining ensures all data is available before the analysis step runs, preventing execution errors."
      },
      "typeVersion": 1
    },
    {
      "id": "ca74487f-6cd7-4811-8d61-a7482a4f7fcb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -7408,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 556,
        "height": 618,
        "content": "## 3. Analysis & Output\n\n**Trend Analysis**\nCompares revenue/orders against history using the defined threshold. Outputs text-based trends.\n**Reporting**\nSends analystic Data to Slack and appends a row to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "2655b0fa-da96-4097-b894-fe68749c7d99",
      "name": "Slack Message",
      "type": "n8n-nodes-base.slack",
      "position": [
        -7120,
        208
      ],
      "parameters": {
        "text": "=*Trend Report*  *REVENUE* \nCurrent:{{ $json.revenue.current }}  \nVs Month: {{ $json.revenue.vsMonth.percent }} ({{ $json.revenue.vsMonth.trend }}) \nVs Year:  {{ $json.revenue.vsYear.percent }} ({{ $json.revenue.vsYear.trend }}) \n-----------------------------------  \n*ORDERS* \nCurrent: {{ $json.orders.current }} \nVs Month: {{ $json.orders.vsMonth.percent }} ({{ $json.orders.vsMonth.trend }})\nVs Year:  {{ $json.orders.vsYear.percent }} ({{ $json.orders.vsYear.trend }}) \n-----------------------------------  \n*Top SKU:* {{ $json.topSku }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0A45F2SK51",
          "cachedResultName": "n8n-message"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "bc822a7d-c28a-44d1-9add-0d2d72cb590e",
  "connections": {
    "Weekly Trigger": {
      "main": [
        [
          {
            "node": "Global Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Global Configuration": {
      "main": [
        [
          {
            "node": "Calculate Date Ranges",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Date Ranges": {
      "main": [
        [
          {
            "node": "Fetch Current Week Sales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Last Year Sales": {
      "main": [
        [
          {
            "node": "Generate Trend Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Trend Report": {
      "main": [
        [
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          },
          {
            "node": "Slack Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Last Month Sales": {
      "main": [
        [
          {
            "node": "Fetch Last Year Sales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Current Week Sales": {
      "main": [
        [
          {
            "node": "Fetch Last Month Sales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}