{
  "id": "9SHBgjcQucHGnZP3",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Demand Forecasting & Auto-Ordering with Optimal Supplier Selection",
  "tags": [],
  "nodes": [
    {
      "id": "ddf6d05b-2054-4c90-9d38-1007c7968185",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        -624
      ],
      "parameters": {
        "width": 420,
        "height": 560,
        "content": "## \ud83d\udcca Workflow Overview\nThis workflow automates inventory replenishment by predicting demand and selecting the best supplier.\n\n## How it works\n1. **Data Collection**: Aggregates sales data (POS, DB), weather, and social trends.\n2. **AI Prediction**: Forecasts 7-day demand to identify stock shortages.\n3. **Supplier Selection**: Requests quotes from multiple vendors and picks the best offer based on cost and lead time.\n4. **Execution**: Places orders automatically and notifies Slack if anomalies are detected.\n\n## Setup steps\n1. **Credentials**: Configure MySQL and Slack credentials.\n2. **API Keys**: Update HTTP nodes with your actual API endpoints/keys for POS, Weather, and Suppliers.\n3. **Database**: Ensure the `forecast_order_log` table exists in MySQL.\n4. **Schedule**: Adjust the Schedule Trigger (default: Daily at 03:00)."
      },
      "typeVersion": 1
    },
    {
      "id": "67f87cf3-be25-44bd-924f-631492e09341",
      "name": "Group: Data Collection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        336,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 1240,
        "height": 288,
        "content": "## Data Collection\nGathers data from POS, database, weather, and SNS trends to build a dataset for analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "67bd99ee-7c29-46a5-b518-d89b7bd35c11",
      "name": "Group: Prediction",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 1580,
        "height": 304,
        "content": "## Prediction & Logic\nGenerates AI demand forecasts, compares against current stock, and calculates exact order quantities."
      },
      "typeVersion": 1
    },
    {
      "id": "ebdd89da-db0c-4ca4-b7fb-d16a57100c53",
      "name": "Group: Selection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3360,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 1240,
        "height": 336,
        "content": "## Supplier Optimization\nRequests quotes from multiple suppliers in parallel and selects the best option based on price and delivery speed."
      },
      "typeVersion": 1
    },
    {
      "id": "0896d4e5-5320-4480-9be8-dbf467bad785",
      "name": "Group: Execution",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4704,
        -64
      ],
      "parameters": {
        "color": 7,
        "width": 1060,
        "height": 352,
        "content": "## Execution & Alerting\nPlaces the purchase order, logs the transaction to the database, and alerts Slack if manual review is needed."
      },
      "typeVersion": 1
    },
    {
      "id": "852f2a43-392f-4970-a10b-941964bb8502",
      "name": "Run Daily at 03:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        384,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 3
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ce62bfeb-84be-4654-a251-e804c13be2f6",
      "name": "Fetch POS Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        608,
        144
      ],
      "parameters": {
        "url": "=https://pos-api.example.com/sales",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "from",
              "value": "={{ $now.minus(3, 'days').format('yyyy-MM-dd') }}"
            },
            {
              "name": "to",
              "value": "={{ $now.format('yyyy-MM-dd') }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2a1e1bc0-a62a-4adb-9fe3-adeb2902fee9",
      "name": "Fetch Historical Sales",
      "type": "n8n-nodes-base.mySql",
      "position": [
        816,
        144
      ],
      "parameters": {
        "operation": "select"
      },
      "typeVersion": 1
    },
    {
      "id": "7be15790-7d94-4f18-b4b4-5df44fe1a418",
      "name": "Fetch Weather Forecast",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1040,
        144
      ],
      "parameters": {
        "url": "=https://api.openweathermap.org/data/2.5/forecast",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "q",
              "value": "Tokyo"
            },
            {
              "name": "appid",
              "value": "YOUR_API_KEY"
            },
            {
              "name": "cnt",
              "value": "5"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7efd68ef-803e-4147-92a8-9052ed0cb019",
      "name": "Fetch SNS Trends",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1264,
        144
      ],
      "parameters": {
        "url": "=https://trend-api.example.com/social/metrics",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "keywords",
              "value": "product_category"
            },
            {
              "name": "period",
              "value": "24h"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "78f21d75-ad56-4a93-8f37-08ff3d3761d2",
      "name": "Fetch Inventory Master",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1488,
        144
      ],
      "parameters": {
        "url": "=https://inventory-api.example.com/stock/current",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "94ba4b53-df63-4766-8e67-520b18a47d53",
      "name": "Merge Data 1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1728,
        144
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "6d1a2427-c2e4-48bb-acd1-dfbdbd4a4aa3",
      "name": "Merge Data 2",
      "type": "n8n-nodes-base.merge",
      "position": [
        1936,
        144
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "834cbef6-6547-458c-94ea-55641d7a0e57",
      "name": "Format Prediction Dataset",
      "type": "n8n-nodes-base.code",
      "position": [
        2160,
        144
      ],
      "parameters": {
        "jsCode": "// Create feature dataset for AI prediction\nconst items = [];\n\nfor (const item of $input.all()) {\n  const features = {\n    productId: item.json.product_id,\n    historicalSales: item.json.quantity_sold || 0,\n    currentStock: item.json.current_stock || 0,\n    weatherScore: item.json.weather_temp || 20,\n    trendScore: item.json.trend_score || 0,\n    dayOfWeek: new Date().getDay(),\n    campaignFlag: item.json.campaign_flag || 0,\n    safetyStock: item.json.safety_stock || 10,\n    leadTime: item.json.lead_time || 3\n  };\n  \n  items.push({json: features});\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "f6af552d-08cf-4324-989d-3e9cf888e2ae",
      "name": "Call AI Prediction API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2384,
        144
      ],
      "parameters": {
        "url": "=https://ai-prediction-api.example.com/forecast",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "features",
              "value": "={{ $json }}"
            },
            {
              "name": "horizon_days",
              "value": "7"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8881fa92-3178-482c-9c43-138b4d09b8ec",
      "name": "Calculate Stock Shortage",
      "type": "n8n-nodes-base.code",
      "position": [
        2608,
        144
      ],
      "parameters": {
        "jsCode": "// Combine prediction results with inventory info and calculate required order quantity\nconst items = [];\n\nfor (const item of $input.all()) {\n  const forecast = item.json.forecastQuantity || 0;\n  const currentStock = item.json.currentStock || 0;\n  const onOrder = item.json.onOrderQuantity || 0;\n  const safetyStock = item.json.safetyStock || 10;\n  \n  const totalRequired = forecast + safetyStock;\n  const totalSupply = currentStock + onOrder;\n  const shortageQty = Math.max(0, totalRequired - totalSupply);\n  \n  items.push({\n    json: {\n      ...item.json,\n      totalRequired,\n      totalSupply,\n      shortageQuantity: shortageQty\n    }\n  });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "653f62ff-7e43-4de5-a63e-1340b7880b5f",
      "name": "Finalize Order Qty",
      "type": "n8n-nodes-base.code",
      "position": [
        2816,
        144
      ],
      "parameters": {
        "jsCode": "// Extract items to order and finalize order quantity\nconst orderItems = [];\n\nfor (const item of $input.all()) {\n  if (item.json.shortageQuantity > 0) {\n    const lotSize = item.json.minOrderLot || 1;\n    const baseQty = Math.ceil(item.json.shortageQuantity);\n    const orderQty = Math.ceil(baseQty / lotSize) * lotSize;\n    \n    orderItems.push({\n      json: {\n        productId: item.json.productId,\n        productName: item.json.productName || 'Unknown',\n        orderQuantity: orderQty,\n        category: item.json.category,\n        leadTime: item.json.leadTime\n      }\n    });\n  }\n}\n\nreturn orderItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "0a040e5e-e822-48c7-9dc6-38154dc4757f",
      "name": "Check Order Necessity",
      "type": "n8n-nodes-base.if",
      "position": [
        3040,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a1b2c3d4-e5f6-7890-abcd-111111111111",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $input.all().length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "5f1235f7-bfda-40bc-8909-3de02a6e2ea2",
      "name": "Split by Product",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3408,
        144
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "a68ddedf-d237-4c93-b67f-05ee899fbd65",
      "name": "Get Quote Supplier A",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3616,
        144
      ],
      "parameters": {
        "url": "=https://supplier-a.example.com/api/quote",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "productId",
              "value": "={{ $json.productId }}"
            },
            {
              "name": "quantity",
              "value": "={{ $json.orderQuantity }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1712f494-286d-41f4-9c80-97d42927a172",
      "name": "Get Quote Supplier B",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3840,
        144
      ],
      "parameters": {
        "url": "=https://supplier-b.example.com/api/pricing",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "item_code",
              "value": "={{ $json.productId }}"
            },
            {
              "name": "qty",
              "value": "={{ $json.orderQuantity }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "945ea5bf-91df-4425-bd09-fddc86475cd3",
      "name": "Get Quote Supplier C",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4064,
        144
      ],
      "parameters": {
        "url": "=https://supplier-c.example.com/quote/request",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "product",
              "value": "={{ $json.productId }}"
            },
            {
              "name": "volume",
              "value": "={{ $json.orderQuantity }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "564e0a09-b694-4785-bb3f-2212df357658",
      "name": "Merge Quotes",
      "type": "n8n-nodes-base.merge",
      "position": [
        4288,
        144
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3
    },
    {
      "id": "7a120dff-b0c6-4bf5-a38d-f6dbd8b86b8a",
      "name": "Select Best Supplier",
      "type": "n8n-nodes-base.code",
      "position": [
        4496,
        144
      ],
      "parameters": {
        "jsCode": "// Logic to select optimal supplier\nconst quotes = $input.all();\nlet bestSupplier = null;\nlet lowestCost = Infinity;\n\nfor (const quote of quotes) {\n  const unitPrice = quote.json.unitPrice || quote.json.price || 0;\n  const shipping = quote.json.shippingFee || quote.json.freight || 0;\n  const qty = quote.json.orderQuantity || 1;\n  const totalCost = (unitPrice * qty) + shipping;\n  const leadTime = quote.json.leadTime || quote.json.deliveryDays || 999;\n  \n  // Select based on minimum cost and lead time within 7 days\n  if (totalCost < lowestCost && leadTime <= 7) {\n    lowestCost = totalCost;\n    bestSupplier = {\n      supplierName: quote.json.supplierName || 'Supplier',\n      unitPrice: unitPrice,\n      totalCost: totalCost,\n      leadTime: leadTime,\n      productId: quote.json.productId,\n      orderQuantity: qty\n    };\n  }\n}\n\nreturn [{json: bestSupplier}];"
      },
      "typeVersion": 2
    },
    {
      "id": "79aae86f-4efd-46a8-80ac-803338b94d5c",
      "name": "Execute Auto Order",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4736,
        144
      ],
      "parameters": {
        "url": "={{ 'https://' + $json.supplierName + '.example.com/api/order' }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "productId",
              "value": "={{ $json.productId }}"
            },
            {
              "name": "quantity",
              "value": "={{ $json.orderQuantity }}"
            },
            {
              "name": "unitPrice",
              "value": "={{ $json.unitPrice }}"
            },
            {
              "name": "requestedDelivery",
              "value": "={{ $now.plus(3, 'days').format('yyyy-MM-dd') }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "43b25e17-da22-4800-867a-657dfcc014e6",
      "name": "Update Inventory System",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4960,
        144
      ],
      "parameters": {
        "url": "=https://inventory-api.example.com/stock/update",
        "method": "PATCH",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "productId",
              "value": "={{ $json.productId }}"
            },
            {
              "name": "onOrderQuantity",
              "value": "={{ $json.orderQuantity }}"
            },
            {
              "name": "orderId",
              "value": "={{ $json.orderId }}"
            },
            {
              "name": "expectedDelivery",
              "value": "={{ $json.expectedDeliveryDate }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7c965cb2-6c15-423c-8320-451b971ff0fc",
      "name": "Save Order Log",
      "type": "n8n-nodes-base.mySql",
      "position": [
        5184,
        144
      ],
      "parameters": {
        "table": "forecast_order_log",
        "columns": "run_date,product_id,product_name,forecast_quantity,current_stock,shortage_quantity,order_quantity,supplier,unit_price,total_cost,expected_delivery,confidence_score",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "2cd5aef1-b634-4078-80d3-3859d731a81f",
      "name": "Check Anomalies",
      "type": "n8n-nodes-base.if",
      "position": [
        5408,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "c9d0e1f2-a3b4-5678-cdef-aaaaaaaaaaaa",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.confidenceScore }}",
              "rightValue": 0.3
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e1aed4dd-850f-4422-b034-629a88cce140",
      "name": "Slack Anomaly Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        5616,
        144
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f **AI Prediction Anomaly Detected**\nProduct ID: {{ $json.productId }}\nConfidence Score: {{ $json.confidenceScore }}\nPredicted Qty: {{ $json.forecastQuantity }}\n\nManager review required.",
        "select": "channel",
        "channelId": {
          "value": "C1234567890"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "f7672cc8-f4c4-49a1-8b9c-f38ae90448c4",
  "connections": {
    "Merge Data 1": {
      "main": [
        [
          {
            "node": "Merge Data 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Data 2": {
      "main": [
        [
          {
            "node": "Format Prediction Dataset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Quotes": {
      "main": [
        [
          {
            "node": "Select Best Supplier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch POS Data": {
      "main": [
        [
          {
            "node": "Fetch Historical Sales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Order Log": {
      "main": [
        [
          {
            "node": "Check Anomalies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Anomalies": {
      "main": [
        [
          {
            "node": "Slack Anomaly Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch SNS Trends": {
      "main": [
        [
          {
            "node": "Fetch Inventory Master",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split by Product": {
      "main": [
        [
          {
            "node": "Get Quote Supplier A",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Auto Order": {
      "main": [
        [
          {
            "node": "Update Inventory System",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Finalize Order Qty": {
      "main": [
        [
          {
            "node": "Check Order Necessity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Daily at 03:00": {
      "main": [
        [
          {
            "node": "Fetch POS Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Quote Supplier A": {
      "main": [
        [
          {
            "node": "Get Quote Supplier B",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Quote Supplier B": {
      "main": [
        [
          {
            "node": "Get Quote Supplier C",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Quote Supplier C": {
      "main": [
        [
          {
            "node": "Merge Quotes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select Best Supplier": {
      "main": [
        [
          {
            "node": "Execute Auto Order",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Order Necessity": {
      "main": [
        [
          {
            "node": "Split by Product",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call AI Prediction API": {
      "main": [
        [
          {
            "node": "Calculate Stock Shortage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Historical Sales": {
      "main": [
        [
          {
            "node": "Fetch Weather Forecast",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Inventory Master": {
      "main": [
        [
          {
            "node": "Merge Data 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Weather Forecast": {
      "main": [
        [
          {
            "node": "Fetch SNS Trends",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Inventory System": {
      "main": [
        [
          {
            "node": "Save Order Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Stock Shortage": {
      "main": [
        [
          {
            "node": "Finalize Order Qty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Prediction Dataset": {
      "main": [
        [
          {
            "node": "Call AI Prediction API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}