This workflow follows the Google Sheets → Googlesheetstrigger 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 →
{
"name": "Daily Sales, Profit/Loss & Best-Seller Tracker",
"nodes": [
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 1819148452,
"mode": "list",
"cachedResultName": "Sales Form Responses ",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=1819148452"
},
"event": "rowAdded",
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTrigger",
"typeVersion": 1,
"position": [
2384,
1552
],
"id": "2407ce68-c8a7-4b46-99b2-1ca6f986d120",
"name": "New Daily Sales Submitted",
"credentials": {
"googleSheetsTriggerOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const menuItems = $('Read Menu Items').all();\n\nconst menuLookup = {};\nfor (const row of menuItems) {\n const name = row.json['Item Name'];\n menuLookup[name] = {\n price: parseFloat(row.json['Selling Price ($)']) || 0,\n cost: parseFloat(row.json['Cost Per Item ($)']) || 0\n };\n}\n\nconst itemFieldMap = {\n 'Espresso - Qty Sold': 'Espresso',\n 'Cappuccino - Qty Sold': 'Cappuccino',\n 'Latte - Qty Sold': 'Latte',\n 'Americano - Qty Sold': 'Americano',\n 'Mocha - Qty Sold': 'Mocha',\n 'Croissant - Qty Sold': 'Croissant',\n 'Bagel with Cream Cheese - Qty Sold': 'Bagel with Cream Cheese'\n};\n\nconst results = [];\n\nfor (const item of $input.all()) {\n const date = item.json['Date'];\n const staff = item.json['Staff'];\n\n const trimmedFormData = {};\n for (const [rawKey, value] of Object.entries(item.json)) {\n trimmedFormData[rawKey.trim()] = value;\n }\n\n for (const [fieldName, itemName] of Object.entries(itemFieldMap)) {\n const qtyRaw = trimmedFormData[fieldName.trim()];\n const qty = parseInt(qtyRaw, 10) || 0;\n\n const pricing = menuLookup[itemName] || { price: 0, cost: 0 };\n const revenue = qty * pricing.price;\n const cost = qty * pricing.cost;\n const profit = revenue - cost;\n\n results.push({\n json: {\n date: date,\n item: itemName,\n qtySold: qty,\n sellingPrice: pricing.price,\n costPerItem: pricing.cost,\n revenue: revenue,\n cost: cost,\n profit: profit,\n staff: staff\n }\n });\n }\n}\n\nreturn results;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2608,
1552
],
"id": "0a37ed8c-06df-45ad-9851-a657dbbe8f23",
"name": "Code in JavaScript"
},
{
"parameters": {
"operation": "appendOrUpdate",
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 663305116,
"mode": "list",
"cachedResultName": "Daily Sales Log",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=663305116"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $json.date }}",
"Item": "={{ $json.item }}",
"Qty Sold": "={{ $json.qtySold }}",
"Staff": "={{ $json.staff }}",
"Selling Price ($)": "={{ $json.sellingPrice }}",
"Cost Per Item ($)": "={{ $json.costPerItem }}",
"Revenue ($)": "={{ $json.revenue }}",
"Cost ($)": "={{ $json.cost }}",
"Profit ($)": "={{ $json.profit }}"
},
"matchingColumns": [
"Date",
"Item"
],
"schema": [
{
"id": "Date",
"displayName": "Date",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Item",
"displayName": "Item",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Qty Sold",
"displayName": "Qty Sold",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Selling Price ($)",
"displayName": "Selling Price ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Cost Per Item ($)",
"displayName": "Cost Per Item ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Revenue ($)",
"displayName": "Revenue ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Cost ($)",
"displayName": "Cost ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Profit ($)",
"displayName": "Profit ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Staff",
"displayName": "Staff",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2832,
1552
],
"id": "f7dcc107-7999-4533-8f25-3e909c616a10",
"name": "Append row in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 663305116,
"mode": "list",
"cachedResultName": "Daily Sales Log",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=663305116"
},
"filtersUI": {
"values": []
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2608,
1744
],
"id": "2e53a28e-33e5-47c3-8990-42118863a854",
"name": "Read Today's Daily Sales Log",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 23
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
2384,
1840
],
"id": "0d1bf5f5-5998-46dd-8335-3a959a278c3d",
"name": "Nightly Rollup Trigger"
},
{
"parameters": {
"operation": "appendOrUpdate",
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 640887184,
"mode": "list",
"cachedResultName": "Best Seller Ranking",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=640887184"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $json.date }}",
"Rank": "={{ $json.rank }}",
"Item": "={{ $json.item }}",
"Qty Sold": "={{ $json.qtySold }}",
"Revenue ($)": "={{ $json.revenue }}"
},
"matchingColumns": [
"Date",
"Item"
],
"schema": [
{
"id": "Date",
"displayName": "Date",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Rank",
"displayName": "Rank",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Item",
"displayName": "Item",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Qty Sold",
"displayName": "Qty Sold",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Revenue ($)",
"displayName": "Revenue ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
3056,
1744
],
"id": "e4b46b97-a318-4e33-9d2d-1f05becd60c7",
"name": "Write Best Seller Ranking",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 757991866,
"mode": "list",
"cachedResultName": "Inventory Log",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=757991866"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2608,
1936
],
"id": "d3172909-486a-4dfd-afc9-fb0c4408ff47",
"name": "Read Today's Inventory Log",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const salesRows = $('Read Today\\'s Daily Sales Log').all();\nconst inventoryRows = $input.all();\n\nfunction toYMD(dateLike) {\n const d = new Date(dateLike);\n if (isNaN(d)) return null;\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nconst todayYMD = toYMD(new Date());\n\n// Filter BOTH datasets to only today's rows before summing anything\nconst todaysSales = salesRows.filter(row => toYMD(row.json['Date']) === todayYMD);\nconst todaysInventory = inventoryRows.filter(row => toYMD(row.json['Timestamp']) === todayYMD);\n\nlet totalRevenue = 0;\nlet totalCost = 0;\nconst itemTotals = {};\n\nfor (const row of todaysSales) {\n const revenue = parseFloat(row.json['Revenue ($)']) || 0;\n const cost = parseFloat(row.json['Cost ($)']) || 0;\n const qty = parseInt(row.json['Qty Sold'], 10) || 0;\n const item = row.json['Item'];\n\n totalRevenue += revenue;\n totalCost += cost;\n\n if (!itemTotals[item]) itemTotals[item] = 0;\n itemTotals[item] += qty;\n}\n\nlet bestSeller = '';\nlet bestSellerQty = 0;\nfor (const [item, qty] of Object.entries(itemTotals)) {\n if (qty > bestSellerQty) {\n bestSeller = item;\n bestSellerQty = qty;\n }\n}\n\nlet totalWaste = 0;\nfor (const row of todaysInventory) {\n totalWaste += parseFloat(row.json['Waste Cost ($)']) || 0;\n}\n\nconst netProfit = totalRevenue - totalCost - totalWaste;\n\nreturn [{\n json: {\n date: todayYMD,\n totalRevenue: totalRevenue,\n totalCost: totalCost,\n totalWaste: totalWaste,\n netProfit: netProfit,\n bestSeller: bestSeller,\n bestSellerQty: bestSellerQty\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2832,
1936
],
"id": "81d9e647-92c7-42e5-aef0-025cf7407f38",
"name": "Calculate Daily Summary"
},
{
"parameters": {
"operation": "appendOrUpdate",
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 819605866,
"mode": "list",
"cachedResultName": "Daily Business Summary",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=819605866"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $json.date }}",
"Total Revenue ($)": "={{ $json.totalRevenue }}",
"Total Cost ($)": "={{ $json.totalCost }}",
"Total Waste ($)": "={{ $json.totalWaste }}",
"Net Profit ($)": "={{ $json.netProfit }}",
"Best Seller": "={{ $json.bestSeller }}",
"Units Sold (Best Seller)": "={{ $json.bestSellerQty }}"
},
"matchingColumns": [
"Date"
],
"schema": [
{
"id": "Date",
"displayName": "Date",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Total Revenue ($)",
"displayName": "Total Revenue ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Total Cost ($)",
"displayName": "Total Cost ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Total Waste ($)",
"displayName": "Total Waste ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Net Profit ($)",
"displayName": "Net Profit ($)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Best Seller",
"displayName": "Best Seller",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Units Sold (Best Seller)",
"displayName": "Units Sold (Best Seller)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
3056,
1936
],
"id": "22c9e122-02ef-4a39-97bc-e9c3eef302d6",
"name": "Write Daily Business Summary",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw",
"mode": "list",
"cachedResultName": "CAFE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 1623927260,
"mode": "list",
"cachedResultName": "Menu Items",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1GNQIPkvCxuyiPsDWpinSQnr1RLc5IDUZELdVFX7czmw/edit#gid=1623927260"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2608,
1360
],
"id": "87e82997-cca2-44b1-9cc8-2daffc294904",
"name": "Read Menu Items",
"executeOnce": true,
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Normalizes a date value (handles both \"2026-07-18\" ISO format and\n// \"7/19/2026\" US format, since Daily Sales Log currently has both \u2014\n// sample rows were typed manually in ISO, n8n-appended rows come from\n// Google Forms in US format).\nfunction normalizeDate(rawDate) {\n if (!rawDate) return null;\n const str = String(rawDate).trim();\n\n // ISO format: YYYY-MM-DD\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(str)) {\n return str;\n }\n\n // US format: M/D/YYYY or MM/DD/YYYY\n const usMatch = str.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})$/);\n if (usMatch) {\n const [, month, day, year] = usMatch;\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n\n return null; // unrecognized format \u2014 excluded, not silently miscounted\n}\n\n// Today's date, straight from the current execution time \u2014 no fragile\n// reference to a specific trigger node needed.\nconst now = new Date();\nconst todayISO = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;\n\nconst allRows = $input.all();\n\n// Filter to only today's rows, using the normalized date comparison\nconst todaysRows = allRows.filter(row => {\n const rowDate = normalizeDate(row.json['Date']);\n return rowDate === todayISO;\n});\n\n// Group by item name, summing Qty Sold and Revenue\nconst grouped = {};\nfor (const row of todaysRows) {\n const item = row.json['Item'];\n const qty = parseFloat(row.json['Qty Sold']) || 0;\n const revenue = parseFloat(row.json['Revenue ($)']) || 0;\n\n if (!grouped[item]) {\n grouped[item] = { item, qtySold: 0, revenue: 0 };\n }\n grouped[item].qtySold += qty;\n grouped[item].revenue += revenue;\n}\n\n// Sort descending by Qty Sold, assign rank to ALL items (not just #1)\nconst ranked = Object.values(grouped).sort((a, b) => b.qtySold - a.qtySold);\n\nconst results = ranked.map((entry, index) => ({\n json: {\n date: todayISO,\n rank: index + 1,\n item: entry.item,\n qtySold: entry.qtySold,\n revenue: Math.round(entry.revenue * 100) / 100\n }\n}));\n\nreturn results;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2832,
1744
],
"id": "9e7ff058-d6f5-45b0-b75c-70ee5c2acda8",
"name": "Group & Rank by Item",
"alwaysOutputData": true
}
],
"connections": {
"New Daily Sales Submitted": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
},
{
"node": "Read Menu Items",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Nightly Rollup Trigger": {
"main": [
[
{
"node": "Read Today's Daily Sales Log",
"type": "main",
"index": 0
},
{
"node": "Read Today's Inventory Log",
"type": "main",
"index": 0
}
]
]
},
"Read Today's Daily Sales Log": {
"main": [
[
{
"node": "Group & Rank by Item",
"type": "main",
"index": 0
}
]
]
},
"Calculate Daily Summary": {
"main": [
[
{
"node": "Write Daily Business Summary",
"type": "main",
"index": 0
}
]
]
},
"Group & Rank by Item": {
"main": [
[
{
"node": "Write Best Seller Ranking",
"type": "main",
"index": 0
}
]
]
},
"Read Today's Inventory Log": {
"main": [
[
{
"node": "Calculate Daily Summary",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "7a817253-7558-4b41-8fce-754d9bcec578",
"nodeGroups": [],
"id": "YwbJOb8nWyLNcS9q",
"tags": []
}
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.
googleSheetsOAuth2ApigoogleSheetsTriggerOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Daily Sales, Profit/Loss & Best-Seller Tracker. Uses googleSheetsTrigger, googleSheets. Event-driven trigger; 11 nodes.
Source: https://github.com/justinrhedgep-star/cafe-autopilot-n8n/blob/main/01-nightly-sales-inventory-tracker.json — 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 n8n workflow watches a Google Sheets appointments list for cancellations, then texts matching waitlist candidates via Twilio to claim the open slot within 20 minutes and, on confirmation, books t
Stickynote Workflow. Uses googleTranslate, googleSheetsTrigger, googleDrive, httpRequest. Event-driven trigger; 22 nodes.
Automate IT asset allocation for new hires with an intelligent, AI-powered workflow 🤖. This automation reads employee data from Google Sheets, determines role-based requirements, and intelligently ass
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
This powerful no-code workflow automates plagiarism detection using Plagiarism Checker AI Powered. Users submit text through Google Sheets. The system checks for duplication using the API, sends a det