This workflow corresponds to n8n.io template #12371 — we link there as the canonical source.
This workflow follows the Google Sheets → Slack recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"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
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
slackApiwooCommerceApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically aggregates sales data, calculates performance trends (Revenue & Orders) against previous months and years, identifies the top-selling SKU and sends a strictly formatted, professional summary report to Slack.
Source: https://n8n.io/workflows/12371/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow automatically collects WooCommerce sales data every day, calculates key sales metrics, sends a clean summary to Slack and logs the same data into Google Sheets for historical tracking. I
This workflow continuously monitors the TikTok Ads Library for new creatives from specific advertisers or keyword searches, scrapes them via Apify, logs them into Google Sheets, and sends concise noti
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Simplify financial oversight with this automated n8n workflow. Triggered daily, it fetches cash flow and expense data from a Google Sheet, analyzes inflows and outflows, validates records, and generat
This workflow is essential for e-commerce store owners, product strategists, and marketing teams who need real-time insight into what their competitors are selling.