{
  "id": "Fgyk456vwePSFyoP",
  "name": "Automate Shopify Inventory Reordering with Predictive Analytics",
  "tags": [
    {
      "id": "BozSnIOP0arZwOpu",
      "name": "tuguidragos.com",
      "createdAt": "2025-12-14T19:49:53.386Z",
      "updatedAt": "2025-12-14T19:49:53.386Z"
    }
  ],
  "nodes": [
    {
      "id": "5c80dabe-2c5c-4494-83ff-5582c12b5b7b",
      "name": "Hourly Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3056,
        464
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "4ff8cf32-8507-4c02-96a1-31c7d12d5a81",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -2880,
        464
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "shopifyStoreUrl",
              "type": "string",
              "value": "https://your-store.myshopify.com"
            },
            {
              "id": "id-2",
              "name": "reorderPointMultiplier",
              "type": "number",
              "value": 1.5
            },
            {
              "id": "id-3",
              "name": "safetyStockDays",
              "type": "number",
              "value": 7
            },
            {
              "id": "id-4",
              "name": "budgetLimit",
              "type": "number",
              "value": 50000
            },
            {
              "id": "id-5",
              "name": "largeOrderThreshold",
              "type": "number",
              "value": 10000
            },
            {
              "id": "id-6",
              "name": "slowMoverThresholdDays",
              "type": "number",
              "value": 90
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "d43787b7-fa03-498b-a18e-c00efd42e2d0",
      "name": "Get Inventory Levels",
      "type": "n8n-nodes-base.shopify",
      "position": [
        -2352,
        -32
      ],
      "parameters": {
        "resource": "inventoryLevel",
        "authentication": "accessToken"
      },
      "typeVersion": 1
    },
    {
      "id": "aee4a064-6758-4170-84b2-bc26245a3e3a",
      "name": "Get Product Details",
      "type": "n8n-nodes-base.shopify",
      "position": [
        -2352,
        176
      ],
      "parameters": {
        "resource": "product",
        "operation": "getAll",
        "returnAll": true,
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "4a9b52a8-ab2f-4bd9-ae09-f928f86155d7",
      "name": "Get Last 30 Days Orders",
      "type": "n8n-nodes-base.shopify",
      "position": [
        -2352,
        384
      ],
      "parameters": {
        "options": {
          "createdAtMin": "={{ $now.minus({ days: 30 }).toISO() }}"
        },
        "operation": "getAll",
        "returnAll": true
      },
      "typeVersion": 1
    },
    {
      "id": "29c8e322-a08f-4cee-a282-2b82eddff268",
      "name": "Update Inventory Fields",
      "type": "n8n-nodes-base.shopify",
      "position": [
        -912,
        880
      ],
      "parameters": {
        "resource": "inventoryLevel"
      },
      "typeVersion": 1
    },
    {
      "id": "1ff716f3-684d-4522-9ca8-21fa184b5475",
      "name": "Read Inventory Master",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2352,
        752
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Inventory Master"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.INVENTORY_MASTER_SHEET_ID || \"<__PLACEHOLDER_VALUE__Inventory Master Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "597cef4d-5a47-4af3-941e-41cae31fe4dd",
      "name": "Read Suppliers",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2352,
        944
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Suppliers"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SUPPLIERS_SHEET_ID || \"<__PLACEHOLDER_VALUE__Suppliers Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "520ad36f-aae0-41d8-8a40-8f3084799311",
      "name": "Read Purchase Order Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2352,
        1136
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "PO Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.PO_LOG_SHEET_ID || \"<__PLACEHOLDER_VALUE__Purchase Order Log Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "e70cf1ca-d6dc-4a16-a409-8c5a7e306ded",
      "name": "Merge All Data Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1824,
        416
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition",
        "numberInputs": 6
      },
      "typeVersion": 3.2
    },
    {
      "id": "4dfebbf6-8cc0-42e4-89b8-b79828f01347",
      "name": "Calculate Sales Velocity",
      "type": "n8n-nodes-base.code",
      "position": [
        -1456,
        480
      ],
      "parameters": {
        "jsCode": "// Calculate Sales Velocity for each SKU\n// Input: Merged data from inventory, products, orders, sheets\n// Output: Items with velocity_7d, velocity_30d, avg_daily_sales\n\nconst items = $input.all();\n\n// Extract order data and inventory data\nconst orders = [];\nconst inventoryMap = new Map();\nconst productMap = new Map();\nconst inventoryMasterMap = new Map();\nconst suppliersMap = new Map();\n\n// Parse input items to separate orders, inventory, and products\nfor (const item of items) {\n  const data = item.json;\n  \n  // Check if this is order data (from Get Last 30 Days Orders)\n  if (data.line_items && Array.isArray(data.line_items)) {\n    orders.push(data);\n  }\n  \n  // Check if this is inventory level data (from Get Inventory Levels)\n  if (data.inventory_item_id && data.location_id) {\n    const inventoryItemId = data.inventory_item_id;\n    inventoryMap.set(inventoryItemId, data);\n  }\n  \n  // Check if this is product data (from Get Product Details)\n  if (data.variants && Array.isArray(data.variants)) {\n    for (const variant of data.variants) {\n      if (variant.sku) {\n        productMap.set(variant.sku, {\n          ...variant,\n          product_title: data.title,\n          product_id: data.id,\n          product_type: data.product_type,\n          vendor: data.vendor\n        });\n        // Also map by inventory_item_id for cross-referencing\n        if (variant.inventory_item_id) {\n          productMap.set(variant.inventory_item_id, {\n            ...variant,\n            product_title: data.title,\n            product_id: data.id,\n            product_type: data.product_type,\n            vendor: data.vendor\n          });\n        }\n      }\n    }\n  }\n  \n  // Check if this is Inventory Master sheet data\n  if (data.SKU && (data.reorder_point || data.safety_stock)) {\n    inventoryMasterMap.set(data.SKU, data);\n  }\n  \n  // Check if this is Suppliers sheet data\n  if (data.supplier_id || data.supplier_name) {\n    const supplierId = data.supplier_id || data.id;\n    if (supplierId) {\n      suppliersMap.set(supplierId, data);\n    }\n  }\n}\n\n// Calculate sales by SKU\nconst salesBySku = new Map();\nconst now = new Date();\nconst sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\nconst thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n\nfor (const order of orders) {\n  const orderDate = new Date(order.created_at);\n  \n  for (const lineItem of order.line_items) {\n    const sku = lineItem.sku;\n    if (!sku) continue;\n    \n    if (!salesBySku.has(sku)) {\n      salesBySku.set(sku, {\n        total_30d: 0,\n        total_7d: 0,\n        total_all: 0,\n        last_sale_date: null\n      });\n    }\n    \n    const sales = salesBySku.get(sku);\n    const quantity = lineItem.quantity || 0;\n    \n    sales.total_all += quantity;\n    \n    if (orderDate >= thirtyDaysAgo) {\n      sales.total_30d += quantity;\n    }\n    \n    if (orderDate >= sevenDaysAgo) {\n      sales.total_7d += quantity;\n    }\n    \n    // Track last sale date\n    if (!sales.last_sale_date || orderDate > new Date(sales.last_sale_date)) {\n      sales.last_sale_date = orderDate.toISOString();\n    }\n  }\n}\n\n// Build output with velocity calculations\nconst outputItems = [];\n\n// Get all unique SKUs from all sources\nconst allSkus = new Set([\n  ...productMap.keys(),\n  ...salesBySku.keys(),\n  ...inventoryMasterMap.keys()\n]);\n\nfor (const sku of allSkus) {\n  // Skip if this is an inventory_item_id (numeric) and we have the actual SKU\n  if (typeof sku === 'number' || /^\\d+$/.test(sku)) {\n    continue;\n  }\n  \n  const product = productMap.get(sku) || {};\n  const sales = salesBySku.get(sku) || { total_7d: 0, total_30d: 0, total_all: 0, last_sale_date: null };\n  const inventoryMaster = inventoryMasterMap.get(sku) || {};\n  \n  // Get inventory level data by matching inventory_item_id\n  const inventoryItemId = product.inventory_item_id;\n  const inventory = inventoryItemId ? inventoryMap.get(inventoryItemId) : {};\n  \n  // Calculate velocities\n  const velocity_7d = sales.total_7d / 7;\n  const velocity_30d = sales.total_30d / 30;\n  const avg_daily_sales = velocity_30d; // Use 30-day as primary average\n  \n  // Get supplier data if available\n  const supplierId = inventoryMaster.supplier_id || product.vendor;\n  const supplier = supplierId ? suppliersMap.get(supplierId) : {};\n  \n  outputItems.push({\n    json: {\n      sku: sku,\n      inventory_item_id: inventoryItemId,\n      location_id: inventory.location_id,\n      available_quantity: inventory.available,\n      current_stock: inventory.available || 0,\n      product_title: product.product_title,\n      product_id: product.product_id,\n      product_type: product.product_type,\n      vendor: product.vendor,\n      variant_id: product.id,\n      price: product.price,\n      sales_7d: sales.total_7d,\n      sales_30d: sales.total_30d,\n      velocity_7d: Math.round(velocity_7d * 100) / 100,\n      velocity_30d: Math.round(velocity_30d * 100) / 100,\n      avg_daily_sales: Math.round(avg_daily_sales * 100) / 100,\n      last_sale_date: sales.last_sale_date,\n      // Include inventory master data\n      safety_stock: inventoryMaster.safety_stock || 0,\n      max_stock: inventoryMaster.max_stock,\n      supplier_id: inventoryMaster.supplier_id || supplierId,\n      // Include supplier data\n      supplier_name: supplier.supplier_name || supplier.name,\n      supplier_email: supplier.supplier_email || supplier.email,\n      lead_time_days: supplier.lead_time_days || 7,\n      moq: supplier.moq || supplier.minimum_order_quantity || 1,\n      unit_cost: supplier.unit_cost || product.price || 0,\n      calculated_at: now.toISOString()\n    }\n  });\n}\n\nreturn outputItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "7bae862d-8108-43dd-ac01-a029762e47b2",
      "name": "Calculate Dynamic Reorder Point",
      "type": "n8n-nodes-base.code",
      "position": [
        -1120,
        480
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Calculate Dynamic Reorder Point\n// Formula: reorder_point = avg_daily_sales * lead_time + safety_stock\n\nconst item = $input.item.json;\n\n// Get avg_daily_sales from previous Calculate Sales Velocity node\nconst avg_daily_sales = item.avg_daily_sales || 0;\n\n// Get lead_time from supplier data (merged from Read Suppliers node)\nconst lead_time = item.lead_time_days || item.supplier_lead_time || 7; // default 7 days\n\n// Get safety_stock from workflow config\nconst safetyStockDays = $('Workflow Configuration').first().json.safetyStockDays || 7;\nconst safety_stock = avg_daily_sales * safetyStockDays;\n\n// Calculate reorder point\nconst reorder_point = Math.ceil(avg_daily_sales * lead_time + safety_stock);\n\n// Get current stock level\nconst current_stock = item.current_stock || item.available_quantity || item.inventory_quantity || 0;\n\n// Determine if reorder is needed\nconst needs_reorder = current_stock <= reorder_point;\n\n// Return enriched item with reorder calculations\nreturn {\n  ...item,\n  reorder_point: reorder_point,\n  current_stock: current_stock,\n  needs_reorder: needs_reorder,\n  avg_daily_sales: avg_daily_sales,\n  lead_time_days: lead_time,\n  safety_stock: safety_stock,\n  days_until_stockout: avg_daily_sales > 0 ? Math.floor(current_stock / avg_daily_sales) : 999,\n  recommended_order_qty: needs_reorder ? Math.ceil(reorder_point - current_stock) : 0\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "39399f85-c850-4bc3-bd83-d4c8549ac673",
      "name": "Check Reorder Point Reached",
      "type": "n8n-nodes-base.if",
      "position": [
        -1104,
        288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "lte"
              },
              "leftValue": "={{ $json.current_stock }}",
              "rightValue": "={{ $json.reorder_point }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "446bc9c0-8cc4-4bf8-9d50-f8c9d4049704",
      "name": "Check High Stockout Risk",
      "type": "n8n-nodes-base.if",
      "position": [
        -496,
        128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.stockout_risk }}",
              "rightValue": "0.7"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a4bbb6f1-c633-4569-9d6a-37d375e27906",
      "name": "Check Warehouse Redistribution Possible",
      "type": "n8n-nodes-base.if",
      "position": [
        0,
        112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $json.warehouse_overstock }}"
            },
            {
              "id": "id-2",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $json.warehouse_understock }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "41973a11-986d-425f-a9f6-6c22a402119f",
      "name": "Check Supplier Availability",
      "type": "n8n-nodes-base.if",
      "position": [
        0,
        496
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.supplier_available }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "084287bf-6a54-425c-9c3f-7e12140613c8",
      "name": "Check Business Day",
      "type": "n8n-nodes-base.if",
      "position": [
        576,
        480
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $now.weekday >= 1 && $now.weekday <= 5 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "cf4d4a4c-fe43-41ff-8532-a598daaf90ed",
      "name": "Check MOQ Met",
      "type": "n8n-nodes-base.if",
      "position": [
        576,
        672
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.order_quantity }}",
              "rightValue": "={{ $json.moq }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3cca4ca1-382f-437a-b684-8cc6c233817d",
      "name": "Check Promotional Period",
      "type": "n8n-nodes-base.if",
      "position": [
        832,
        656
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.is_promotional_period }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "d479aaa4-30c4-4f11-8bef-b1fa4d4180c4",
      "name": "Check Budget Limit",
      "type": "n8n-nodes-base.if",
      "position": [
        576,
        864
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.total_po_value }}",
              "rightValue": "={{ $('Workflow Configuration').first().json.budgetLimit }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "33782cfd-eb06-487e-b8a9-30eb9ac0e1aa",
      "name": "Check Large Order Approval Needed",
      "type": "n8n-nodes-base.if",
      "position": [
        832,
        848
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.total_po_value > $('Workflow Configuration').first().json.largeOrderThreshold }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "92b2309b-3af2-4515-ae50-f416e4a083fa",
      "name": "Multi-Warehouse Distribution Logic",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        96
      ],
      "parameters": {
        "jsCode": "// Multi-Warehouse Distribution Logic\n// Analyzes inventory across warehouses to identify transfer opportunities\n\nconst items = $input.all();\nconst transferRecommendations = [];\n\n// Group inventory by SKU and warehouse\nconst inventoryBySku = {};\n\nfor (const item of items) {\n  const sku = item.json.sku;\n  const warehouse = item.json.warehouse || 'main';\n  const currentStock = item.json.current_stock || 0;\n  const reorderPoint = item.json.reorder_point || 0;\n  const maxStock = item.json.max_stock || reorderPoint * 3;\n  \n  if (!inventoryBySku[sku]) {\n    inventoryBySku[sku] = {};\n  }\n  \n  inventoryBySku[sku][warehouse] = {\n    current_stock: currentStock,\n    reorder_point: reorderPoint,\n    max_stock: maxStock,\n    overstock: Math.max(0, currentStock - maxStock),\n    understock: Math.max(0, reorderPoint - currentStock)\n  };\n}\n\n// Analyze each SKU for transfer opportunities\nfor (const sku in inventoryBySku) {\n  const warehouses = inventoryBySku[sku];\n  const warehouseNames = Object.keys(warehouses);\n  \n  // Compare each pair of warehouses\n  for (let i = 0; i < warehouseNames.length; i++) {\n    for (let j = 0; j < warehouseNames.length; j++) {\n      if (i === j) continue;\n      \n      const fromWarehouse = warehouseNames[i];\n      const toWarehouse = warehouseNames[j];\n      const from = warehouses[fromWarehouse];\n      const to = warehouses[toWarehouse];\n      \n      // Check if warehouse A has overstock and warehouse B has understock\n      if (from.overstock > 0 && to.understock > 0) {\n        // Calculate optimal transfer quantity\n        // Transfer the minimum of: overstock amount, understock need, or safe transfer amount\n        const safeTransferAmount = Math.floor(from.current_stock - from.reorder_point);\n        const transferQuantity = Math.min(\n          from.overstock,\n          to.understock,\n          Math.max(0, safeTransferAmount)\n        );\n        \n        if (transferQuantity > 0) {\n          transferRecommendations.push({\n            json: {\n              sku: sku,\n              from_warehouse: fromWarehouse,\n              to_warehouse: toWarehouse,\n              transfer_quantity: transferQuantity,\n              from_current_stock: from.current_stock,\n              from_after_transfer: from.current_stock - transferQuantity,\n              to_current_stock: to.current_stock,\n              to_after_transfer: to.current_stock + transferQuantity,\n              priority: to.understock / to.reorder_point, // Higher priority for more critical understocks\n              estimated_cost_savings: transferQuantity * 10, // Placeholder for cost calculation\n              recommendation_date: new Date().toISOString()\n            }\n          });\n        }\n      }\n    }\n  }\n}\n\n// Sort by priority (highest first)\ntransferRecommendations.sort((a, b) => b.json.priority - a.json.priority);\n\nreturn transferRecommendations.length > 0 ? transferRecommendations : [{ json: { message: 'No transfer recommendations at this time' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "10a4acc0-f331-47ef-959a-03772f3b23d7",
      "name": "Structure PO Line Items",
      "type": "n8n-nodes-base.set",
      "position": [
        -544,
        1536
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "po_line_item_id",
              "type": "string",
              "value": "={{ $json.sku }}_{{ $now.toMillis() }}"
            },
            {
              "id": "id-2",
              "name": "supplier_id",
              "type": "string",
              "value": "={{ $json.supplier_id }}"
            },
            {
              "id": "id-3",
              "name": "sku",
              "type": "string",
              "value": "={{ $json.sku }}"
            },
            {
              "id": "id-4",
              "name": "quantity",
              "type": "number",
              "value": "={{ $json.order_quantity }}"
            },
            {
              "id": "id-5",
              "name": "unit_price",
              "type": "number",
              "value": "={{ $json.unit_price }}"
            },
            {
              "id": "id-6",
              "name": "total_price",
              "type": "number",
              "value": "={{ $json.order_quantity * $json.unit_price }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "745f377c-6d25-4fb3-af5e-82a60aa68e2e",
      "name": "Structure Inventory Updates",
      "type": "n8n-nodes-base.set",
      "position": [
        -1104,
        880
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "inventory_item_id",
              "type": "string",
              "value": "={{ $json.inventory_item_id }}"
            },
            {
              "id": "id-2",
              "name": "location_id",
              "type": "string",
              "value": "={{ $json.location_id }}"
            },
            {
              "id": "id-3",
              "name": "available",
              "type": "number",
              "value": "={{ $json.reorder_point }}"
            },
            {
              "id": "id-4",
              "name": "updated_at",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "ee4cb73c-0934-46b3-b449-94224da55324",
      "name": "Structure Analytics Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -544,
        688
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "inventory_turnover",
              "type": "number",
              "value": "={{ $json.velocity_30d / $json.current_stock }}"
            },
            {
              "id": "id-2",
              "name": "carrying_cost",
              "type": "number",
              "value": "={{ $json.current_stock * $json.unit_price * 0.25 }}"
            },
            {
              "id": "id-3",
              "name": "stockout_saves",
              "type": "number",
              "value": "={{ $json.prevented_stockouts * $json.avg_order_value }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "fa5926c9-26e5-4832-8782-07a4aa06e492",
      "name": "Structure Transfer Recommendations",
      "type": "n8n-nodes-base.set",
      "position": [
        416,
        96
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "transfer_id",
              "type": "string",
              "value": "={{ $json.sku }}_transfer_{{ $now.toMillis() }}"
            },
            {
              "id": "id-2",
              "name": "from_warehouse",
              "type": "string",
              "value": "={{ $json.from_warehouse }}"
            },
            {
              "id": "id-3",
              "name": "to_warehouse",
              "type": "string",
              "value": "={{ $json.to_warehouse }}"
            },
            {
              "id": "id-4",
              "name": "sku",
              "type": "string",
              "value": "={{ $json.sku }}"
            },
            {
              "id": "id-5",
              "name": "transfer_quantity",
              "type": "number",
              "value": "={{ $json.transfer_quantity }}"
            },
            {
              "id": "id-6",
              "name": "created_at",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "36367dbd-e441-4fd2-bc82-3265a17c6db5",
      "name": "Structure Slow-Mover Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1344,
        1232
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "sku",
              "type": "string",
              "value": "={{ $json.sku }}"
            },
            {
              "id": "id-2",
              "name": "days_no_sales",
              "type": "number",
              "value": "={{ $json.days_no_sales }}"
            },
            {
              "id": "id-3",
              "name": "current_stock",
              "type": "number",
              "value": "={{ $json.current_stock }}"
            },
            {
              "id": "id-4",
              "name": "suggested_discount",
              "type": "string",
              "value": "={{ $json.suggested_discount }}"
            },
            {
              "id": "id-5",
              "name": "suggested_bundle",
              "type": "string",
              "value": "={{ $json.suggested_bundle }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "2168e9de-1d54-416b-83cd-d24bf23ea109",
      "name": "Prepare PO Email Context",
      "type": "n8n-nodes-base.code",
      "position": [
        -368,
        1536
      ],
      "parameters": {
        "jsCode": "// Prepare PO Email Context\n// Group line items by supplier, calculate totals, format supplier details\n\nconst items = $input.all();\nconst posBySupplier = {};\n\n// Group items by supplier\nfor (const item of items) {\n  const data = item.json;\n  const supplierId = data.supplier_id || data.supplierId;\n  const supplierName = data.supplier_name || data.supplierName || 'Unknown Supplier';\n  const supplierEmail = data.supplier_email || data.supplierEmail || '';\n  const supplierApiEndpoint = data.supplier_api_endpoint || data.supplierApiEndpoint || '';\n  \n  if (!posBySupplier[supplierId]) {\n    posBySupplier[supplierId] = {\n      supplier_id: supplierId,\n      supplier_name: supplierName,\n      supplier_email: supplierEmail,\n      supplier_api_endpoint: supplierApiEndpoint,\n      line_items: [],\n      total_amount: 0\n    };\n  }\n  \n  // Add line item\n  const lineItem = {\n    sku: data.sku || data.product_sku,\n    product_name: data.product_name || data.productName,\n    quantity: data.order_quantity || data.orderQuantity || 0,\n    unit_price: data.unit_cost || data.unitCost || 0,\n    line_total: (data.order_quantity || 0) * (data.unit_cost || 0)\n  };\n  \n  posBySupplier[supplierId].line_items.push(lineItem);\n  posBySupplier[supplierId].total_amount += lineItem.line_total;\n}\n\n// Convert to array and generate PO numbers\nconst result = [];\nlet poCounter = 1;\nconst now = new Date();\nconst dateStr = now.toISOString().split('T')[0].replace(/-/g, '');\n\nfor (const supplierId in posBySupplier) {\n  const po = posBySupplier[supplierId];\n  po.po_number = `PO-${dateStr}-${String(poCounter).padStart(4, '0')}`;\n  po.po_date = now.toISOString().split('T')[0]; // Add PO date in YYYY-MM-DD format\n  po.total_amount = Math.round(po.total_amount * 100) / 100; // Round to 2 decimals\n  \n  // Format line items as HTML table rows\n  const lineItemsHtml = po.line_items.map(item => {\n    return `<tr>\n      <td>${item.sku}</td>\n      <td>${item.product_name}</td>\n      <td>${item.quantity}</td>\n      <td>$${item.unit_price.toFixed(2)}</td>\n      <td>$${item.line_total.toFixed(2)}</td>\n    </tr>`;\n  }).join('\\n');\n  \n  po.line_items = lineItemsHtml;\n  \n  // Add default delivery instructions\n  po.delivery_instructions = 'Please deliver to our main warehouse during business hours (9 AM - 5 PM, Monday-Friday). Contact receiving department at least 24 hours before delivery.';\n  \n  result.push({ json: po });\n  poCounter++;\n}\n\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "c761423d-acc4-4b07-a392-0b9c76c51da7",
      "name": "Send PO Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        144,
        1232
      ],
      "parameters": {
        "sendTo": "={{ $json.supplier_email || \"<__PLACEHOLDER_VALUE__Supplier email address__>\" }}",
        "message": "=<h2>Purchase Order</h2>\n<p><strong>Supplier:</strong> {{ $json.supplier_name }}</p>\n<p><strong>PO Number:</strong> {{ $json.po_number }}</p>\n<p><strong>Date:</strong> {{ $json.po_date }}</p>\n\n<h3>Line Items</h3>\n<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%;\">\n  <thead>\n    <tr style=\"background-color: #f2f2f2;\">\n      <th>SKU</th>\n      <th>Product Name</th>\n      <th>Quantity</th>\n      <th>Unit Price</th>\n      <th>Total</th>\n    </tr>\n  </thead>\n  <tbody>\n    {{ $json.line_items }}\n  </tbody>\n</table>\n\n<p><strong>Total Amount:</strong> ${{ $json.total_amount.toFixed(2) }}</p>\n\n<h3>Delivery Instructions</h3>\n<p>{{ $json.delivery_instructions }}</p>\n\n<p>Please confirm receipt of this purchase order.</p>\n<p>Thank you for your business.</p>",
        "options": {},
        "subject": "=Purchase Order - {{ $json.po_number }}",
        "authentication": "serviceAccount"
      },
      "typeVersion": 2.1
    },
    {
      "id": "c31bd278-c720-4c06-b0f5-0cba015d1782",
      "name": "Send PO to Supplier API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        144,
        1648
      ],
      "parameters": {
        "url": "={{ $json.supplier_api_endpoint || \"<__PLACEHOLDER_VALUE__Supplier API endpoint__>\" }}",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "po_number",
              "value": "={{ $json.po_number }}"
            },
            {
              "name": "line_items",
              "value": "={{ $json.line_items }}"
            },
            {
              "name": "total_amount",
              "value": "={{ $json.total_amount }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{ $env.SUPPLIER_API_TOKEN || \"<__PLACEHOLDER_VALUE__Supplier API token__>\" }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "f506f27e-5e39-45d8-af1e-6087fe441c00",
      "name": "Wait for PO Confirmation",
      "type": "n8n-nodes-base.wait",
      "position": [
        608,
        1232
      ],
      "parameters": {
        "unit": "hours",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "6ba4434b-77a1-4ff4-97d4-e4a1246cf926",
      "name": "Update Purchase Order Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        816,
        1232
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "sent",
            "po_number": "={{ $json.po_number }}",
            "created_at": "={{ $json.created_at || $now.toISO() }}",
            "supplier_id": "={{ $json.supplier_id }}",
            "confirmed_at": "={{ $json.confirmed_at || \"\" }}",
            "total_amount": "={{ $json.total_amount }}"
          },
          "schema": [
            {
              "id": "po_number",
              "required": false,
              "displayName": "po_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "supplier_id",
              "required": false,
              "displayName": "supplier_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_amount",
              "required": false,
              "displayName": "total_amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "confirmed_at",
              "required": false,
              "displayName": "confirmed_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "po_number"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "PO Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.PO_LOG_SHEET_ID || \"<__PLACEHOLDER_VALUE__Purchase Order Log Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "86c29c8b-d48b-4f3c-bcd4-e117071d940d",
      "name": "Alert Critical Stock Risk",
      "type": "n8n-nodes-base.slack",
      "position": [
        -688,
        -80
      ],
      "parameters": {
        "text": "=\ud83d\udea8 CRITICAL STOCK RISK: SKU {{ $json.sku }} has high stockout risk ({{ $json.stockout_risk }}). Current stock: {{ $json.current_stock }}, Reorder point: {{ $json.reorder_point }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_CRITICAL_CHANNEL || \"<__PLACEHOLDER_VALUE__Slack channel ID for critical alerts__>\" }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "e3eec370-7613-4b01-bb10-8bc74c0744bb",
      "name": "Alert PO Sent",
      "type": "n8n-nodes-base.slack",
      "position": [
        144,
        1456
      ],
      "parameters": {
        "text": "=\u2705 Purchase Order Sent: PO #={{ $json.po_number }} to {{ $json.supplier_name }} for $={{ $json.total_amount }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_PO_CHANNEL || \"<__PLACEHOLDER_VALUE__Slack channel ID for PO notifications__>\" }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "50187ac0-c76c-4f5e-80a9-d1b33d17e84f",
      "name": "Alert Slow-Mover Suggestions",
      "type": "n8n-nodes-base.slack",
      "position": [
        -1120,
        1232
      ],
      "parameters": {
        "text": "=\ud83d\udce6 Slow-Mover Alert: SKU {{ $json.sku }} has not sold in {{ $json.days_no_sales }} days. Current stock: {{ $json.current_stock }}. Suggested action: {{ $json.suggested_discount }}% discount or bundle with {{ $json.suggested_bundle }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_SLOWMOVER_CHANNEL || \"<__PLACEHOLDER_VALUE__Slack channel ID for slow-mover alerts__>\" }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "08fdf235-0d62-46bc-be46-c70b96a4e3d8",
      "name": "Send Daily Summary",
      "type": "n8n-nodes-base.slack",
      "position": [
        1280,
        1232
      ],
      "parameters": {
        "text": "=\ud83d\udcca Daily Inventory Summary: Total POs sent: {{ $json.total_pos }}, Total value: ${{ $json.total_value }}, Critical alerts: {{ $json.critical_alerts }}, Slow-movers identified: {{ $json.slow_movers_count }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_SUMMARY_CHANNEL || \"<__PLACEHOLDER_VALUE__Slack channel ID for daily summary__>\" }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "b6ea39ac-7159-4d73-a4a3-c3c1a3fcfc87",
      "name": "Sync to Accounting System",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -272,
        688
      ],
      "parameters": {
        "url": "={{ $env.ACCOUNTING_API_ENDPOINT || \"<__PLACEHOLDER_VALUE__Accounting API endpoint URL__>\" }}",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "total_inventory_value",
              "value": "={{ $json.total_inventory_value }}"
            },
            {
              "name": "carrying_costs",
              "value": "={{ $json.carrying_costs }}"
            },
            {
              "name": "turnover_rate",
              "value": "={{ $json.turnover_rate }}"
            },
            {
              "name": "timestamp",
              "value": "={{ $json.timestamp }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "c4525794-44be-4fd1-826a-c5980dca1531",
      "name": "Write Dashboard Metrics",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -272,
        528
      ],
      "parameters": {
        "columns": {
          "value": {
            "timestamp": "={{ $json.timestamp }}",
            "carrying_cost": "={{ $json.carrying_cost }}",
            "stockout_saves": "={{ $json.stockout_saves }}",
            "inventory_turnover": "={{ $json.inventory_turnover }}",
            "total_inventory_value": "={{ $json.total_inventory_value }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "inventory_turnover",
              "required": false,
              "displayName": "inventory_turnover",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "carrying_cost",
              "required": false,
              "displayName": "carrying_cost",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "stockout_saves",
              "required": false,
              "displayName": "stockout_saves",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_inventory_value",
              "required": false,
              "displayName": "total_inventory_value",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "timestamp"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Metrics"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.DASHBOARD_METRICS_SHEET_ID || \"<__PLACEHOLDER_VALUE__Dashboard Metrics Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "e8d45593-db32-4f38-88a7-5b90090bcf3b",
      "name": "Write Scenario Planning",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -272,
        848
      ],
      "parameters": {
        "columns": {
          "value": {
            "sku": "={{ $json.sku }}",
            "lead_time": "={{ $json.lead_time }}",
            "forecast_30d": "={{ $json.forecast_30d }}",
            "recommended_stock_promo": "={{ $json.recommended_stock_promo }}",
            "recommended_stock_normal": "={{ $json.recommended_stock_normal }}"
          },
          "schema": [
            {
              "id": "sku",
              "required": false,
              "displayName": "sku",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "recommended_stock_promo",
              "required": false,
              "displayName": "recommended_stock_promo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "recommended_stock_normal",
              "required": false,
              "displayName": "recommended_stock_normal",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "forecast_30d",
              "required": false,
              "displayName": "forecast_30d",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_time",
              "required": false,
              "displayName": "lead_time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "sku"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Scenarios"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SCENARIO_PLANNING_SHEET_ID || \"<__PLACEHOLDER_VALUE__Scenario Planning Google Sheet ID__>\" }}"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "840774b2-f2ee-4022-840c-b60fcfea51a8",
      "name": "Detect Slow-Movers",
      "type": "n8n-nodes-base.code",
      "position": [
        -1568,
        1232
      ],
      "parameters": {
        "jsCode": "// Detect slow-moving inventory items and generate actionable recommendations\n\nconst items = $input.all();\nconst slowMovers = [];\n\n// Configuration\nconst SLOW_MOVER_THRESHOLD = 2; // velocity below this is considered slow\nconst today = new Date();\n\nfor (const item of items) {\n  const velocity = item.json.velocity_30d || 0;\n  const lastSaleDate = item.json.last_sale_date ? new Date(item.json.last_sale_date) : null;\n  \n  // Check if item is a slow mover\n  if (velocity < SLOW_MOVER_THRESHOLD) {\n    // Calculate days since last sale\n    let daysSinceLastSale = 0;\n    if (lastSaleDate) {\n      const diffTime = Math.abs(today - lastSaleDate);\n      daysSinceLastSale = Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n    } else {\n      daysSinceLastSale = 90; // Default if no sale date available\n    }\n    \n    // Calculate suggested discount based on days without sales\n    let suggestedDiscount = 10; // Base discount\n    if (daysSinceLastSale > 60) {\n      suggestedDiscount = 30;\n    } else if (daysSinceLastSale > 30) {\n      suggestedDiscount = 20;\n    } else if (daysSinceLastSale > 14) {\n      suggestedDiscount = 15;\n    }\n    \n    // Suggest bundle partners based on category\n    const category = item.json.category || item.json.product_type || 'General';\n    const sku = item.json.sku || item.json.product_id;\n    \n    // Find potential bundle partners from same category with better velocity\n    const bundlePartners = items\n      .filter(i => {\n        const iCategory = i.json.category || i.json.product_type || 'General';\n        const iVelocity = i.json.velocity_30d || 0;\n        const iSku = i.json.sku || i.json.product_id;\n        return iCategory === category && iVelocity > velocity && iSku !== sku;\n      })\n      .slice(0, 3)\n      .map(i => i.json.sku || i.json.product_id || i.json.title)\n      .join(', ');\n    \n    slowMovers.push({\n      json: {\n        ...item.json,\n        days_no_sales: daysSinceLastSale,\n        suggested_discount: suggestedDiscount,\n        suggested_bundle: bundlePartners || 'No suitable bundle partners found',\n        slow_mover_flag: true,\n        recommendation_date: today.toISOString()\n      }\n    });\n  }\n}\n\n// Return slow movers or empty array if none found\nreturn slowMovers.length > 0 ? slowMovers : [{ json: { message: 'No slow-moving items detected' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "919a409f-0c67-40d4-8883-8d6099d52406",
      "name": "Optimize Profit Priority",
      "type": "n8n-nodes-base.code",
      "position": [
        1328,
        832
      ],
      "parameters": {
        "jsCode": "// Optimize Profit Priority\n// Calculate profit score and prioritize SKUs based on profitability\n\nconst items = $input.all();\nconst optimizedItems = [];\n\nfor (const item of items) {\n  const data = item.json;\n  \n  // Extract relevant metrics with defaults - using correct field names from previous nodes\n  const unitPrice = data.unit_price || data.unit_cost || 0;\n  const unitCost = data.unit_cost || data.supplier_unit_cost || unitPrice * 0.6; // Estimate if not available\n  const margin = unitPrice > 0 ? (unitPrice - unitCost) / unitPrice : 0;\n  \n  // Use velocity_30d from Calculate Sales Velocity node\n  const velocity = data.velocity_30d || data.avg_daily_sales || 0;\n  \n  // Use carrying_cost if available, otherwise calculate as percentage of unit cost\n  const carryingCost = data.carrying_cost || (data.current_stock * unitCost * 0.25) || 0.01;\n  \n  // Use stockout_risk from Calculate Stockout Risk node\n  const stockoutRisk = data.stockout_risk || 0.01;\n  \n  // Calculate profit score: (margin * velocity) / (carrying_cost + stockout_risk)\n  // Higher margin and velocity increase score, higher costs and risk decrease it\n  const profitScore = velocity > 0 ? (margin * velocity) / (carryingCost + stockoutRisk) : 0;\n  \n  optimizedItems.push({\n    ...data,\n    profit_score: Math.round(profitScore * 100) / 100,\n    margin: Math.round(margin * 10000) / 100, // Convert to percentage with 2 decimals\n    velocity: velocity,\n    carrying_cost: Math.round(carryingCost * 100) / 100,\n    stockout_risk: Math.round(stockoutRisk * 100) / 100\n  });\n}\n\n// Sort by profit_score descending (highest profit priority first)\noptimizedItems.sort((a, b) => b.profit_score - a.profit_score);\n\n// Add priority rank\nconst rankedItems = optimizedItems.map((item, index) => ({\n  json: {\n    ...item,\n    priority_rank: index + 1,\n    priority_tier: index < optimizedItems.length * 0.2 ? 'High' : \n                   index < optimizedItems.length * 0.5 ? 'Medium' : 'Low'\n  }\n}));\n\nreturn rankedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "13c5f844-a067-4106-af5f-4000d3c91524",
      "name": "Calculate Stockout Risk",
      "type": "n8n-nodes-base.code",
      "position": [
        -688,
        128
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Calculate Stockout Risk\n// Assess risk level based on days until stockout vs lead time\n\nconst item = $input.item.json;\n\n// Get days until stockout and lead time\nconst daysUntilStockout = item.days_until_stockout || 999;\nconst leadTime = item.lead_time_days || 7;\n\n// Calculate stockout risk score (0-1 scale)\nlet stockoutRisk = 0;\n\nif (daysUntilStockout <= leadTime) {\n  // Critical: Will stockout before next order arrives\n  stockoutRisk = 0.9;\n} else if (daysUntilStockout <= leadTime * 1.5) {\n  // High: Very close to lead time threshold\n  stockoutRisk = 0.7;\n} else if (daysUntilStockout <= leadTime * 2) {\n  // Medium: Some buffer but still concerning\n  stockoutRisk = 0.5;\n} else if (daysUntilStockout <= leadTime * 3) {\n  // Low-Medium: Reasonable buffer\n  stockoutRisk = 0.3;\n} else {\n  // Low: Plenty of stock\n  stockoutRisk = 0.1;\n}\n\n// Return item with stockout risk score\nreturn {\n  ...item,\n  stockout_risk: stockoutRisk\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8650d25c-8ff9-44b7-84bd-527b517cb277",
      "name": "Enrich with Supplier Data",
      "type": "n8n-nodes-base.code",
      "position": [
        240,
        480
      ],
      "parameters": {
        "jsCode": "// Enrich with Supplier Data\n// Match items with supplier information and add supplier fields\n\nconst items = $input.all();\nconst suppliers = $('Read Suppliers').all();\nconst supplierMap = new Map();\n\n// Build supplier lookup map with multiple ID field variations\nfor (const supplier of suppliers) {\n  const data = supplier.json;\n  const supplierId = data.supplier_id || data.id || data.supplierId || data.ID;\n  \n  if (supplierId) {\n    supplierMap.set(String(supplierId).trim(), data);\n  }\n}\n\n// Enrich each item with supplier data\nconst enrichedItems = items.map(item => {\n  const itemData = item.json;\n  \n  // Try multiple supplier ID field variations\n  const supplierId = String(\n    itemData.supplier_id || \n    itemData.preferred_supplier_id || \n    itemData.supplierId || \n    itemData.preferredSupplierId ||\n    ''\n  ).trim();\n  \n  // Get supplier data from map\n  const supplier = supplierMap.get(supplierId) || {};\n  \n  return {\n    json: {\n      ...itemData,\n      supplier_id: supplierId || itemData.supplier_id,\n      supplier_name: supplier.supplier_name || supplier.name || supplier.supplierName || 'Unknown Supplier',\n      supplier_email: supplier.supplier_email || supplier.email || supplier.supplierEmail || '',\n      supplier_api_endpoint: supplier.api_endpoint || supplier.apiEndpoint || supplier.supplier_api_endpoint || '',\n      supplier_available: supplier.available !== false && supplier.is_active !== false,\n      lead_time_days: supplier.lead_time_days || supplier.leadTimeDays || supplier.lead_time || 7,\n      moq: supplier.moq || supplier.minimum_order_quantity || supplier.minimumOrderQuantity || supplier.MOQ || 1,\n      unit_cost: supplier.unit_cost || supplier.unitCost || supplier.cost || itemData.unit_price || itemData.unitPrice || 0\n    }\n  };\n});\n\nreturn enrichedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "e8b15fd5-f61f-4009-84e9-ac3802d76449",
      "name": "Calculate Order Quantity and Value",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        832
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Calculate Order Quantity and Value\n// Ensures MOQ is met and calculates total PO value\n\nconst item = $input.item.json;\n\n// Get recommended order quantity\nconst orderQuantity = item.recommended_order_qty || 0;\n\n// Get unit cost from supplier data\nconst unitCost = item.unit_cost || item.unit_price || 0;\n\n// Get minimum order quantity\nconst moq = item.moq || 1;\n\n// Adjust quantity to meet MOQ\nconst adjustedQuantity = Math.max(orderQuantity, moq);\n\n// Calculate total PO value\nconst totalPoValue = adjustedQuantity * unitCost;\n\n// Get current timestamp\nconst now = new Date();\n\n// Return enriched item with order calculations\nreturn {\n  ...item,\n  order_quantity: adjustedQuantity,\n  unit_price: unitCost,\n  total_po_value: Math.round(totalPoValue * 100) / 100,\n  po_date: now.toISOString(),\n  created_at: now.toISOString(),\n  status: 'pending'\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "56a1c2a6-2273-4462-af07-e39f56cb7bac",
      "name": "Aggregate Daily Summary",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        1232
      ],
      "parameters": {
        "jsCode": "// Aggregate Daily Summary with safe error handling\n\nlet totalPos = 0;\nlet totalValue = 0;\nlet criticalAlerts = 0;\nlet slowMovers = 0;\n\n// Safely get PO items\ntry {\n  const poItems = $('Update Purchase Order Log').all();\n  for (const po of poItems) {\n    totalPos++;\n    totalValue += po.json.total_amount || 0;\n  }\n} catch (error) {\n  console.log('Update Purchase Order Log node not executed or has no data');\n}\n\n// Safely get critical alerts count\ntry {\n  const criticalAlertItems = $('Alert Critical Stock Risk').all();\n  criticalAlerts = criticalAlertItems.length;\n} catch (error) {\n  console.log('Alert Critical Stock Risk node not executed or has no data');\n}\n\n// Safely get slow movers count\ntry {\n  const slowMoverItems = $('Alert Slow-Mover Suggestions').all();\n  slowMovers = slowMoverItems.length;\n} catch (error) {\n  console.log('Alert Slow-Mover Suggestions node not executed or has no data');\n}\n\nreturn [{\n  json: {\n    total_pos: totalPos,\n    total_value: Math.round(totalValue * 100) / 100,\n    critical_alerts: criticalAlerts,\n    slow_movers_count: slowMovers,\n    summary_date: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "e7643129-c7de-49aa-b2d1-81f0c7b121f5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3840,
        -224
      ],
      "parameters": {
        "width": 1200,
        "height": 528,
        "content": "## How it works\nThis workflow automates inventory monitoring, predictive reordering, and supplier communication using Shopify + Google Sheets.\n\nIt runs hourly to pull live inventory levels, product details, and recent order history from Shopify. In parallel, it reads your Inventory Master, Supplier list, and Purchase Order Log from Google Sheets, then merges all sources into a single SKU-level dataset.\n\nFor each SKU, it calculates sales velocity (7/30-day) and derives a dynamic reorder point based on demand, lead time, and safety stock settings. If current stock drops below the reorder point, it evaluates stockout risk and sends a Slack alert for critical items. Where possible, it checks multi-warehouse redistribution logic to prevent stockouts before ordering.\n\nWhen replenishment is needed, it enriches each SKU with supplier data, verifies availability, calculates optimal order quantity/value, and applies business rules such as MOQ checks, budget limits, and large-order approval thresholds. Items can also be prioritized by profitability to maximize ROI under budget constraints.\n\nThe workflow then structures Purchase Order line items and automatically sends the PO via email and/or a supplier API. After waiting for confirmation, it updates the PO log. It also detects slow-moving products, proposes actions (discounts/bundles), syncs analytics to dashboards/accounting, and sends a daily Slack summary of POs, alerts, and slow-mover suggestions.\n\n## Setup steps\n1. Connect Shopify and set store URL + API credentials in **Workflow Configuration**.\n2. Connect Google Sheets and map the Inventory Master, Suppliers, and PO Log sheets.\n3. Connect Slack and select channels for critical alerts and daily summaries.\n4. Configure lead times, safety stock days, budget/MOQ rules, and approval thresholds.\n5. Run a test with a few SKUs, verify PO creation/logging, then enable the hourly trigger."
      },
      "typeVersion": 1
    },
    {
      "id": "1b6471d0-9a5b-4c3d-98af-2cc25dec465f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3152,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 288,
        "content": "## Trigger & Configuration\nRuns hourly and loads all business parameters (lead time, safety stock, budget/MOQ rules, thresholds). Keeps the workflow adjustable without editing core nodes."
      },
      "typeVersion": 1
    },
    {
      "id": "b1642627-6d3e-4b6a-bed4-9740b59dd96c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2480,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 704,
        "content": "## Shopify Data Sources\nPulls live inventory, product details, and recent order history used to compute demand, risk, and reorder decisions per SKU."
      },
      "typeVersion": 1
    },
    {
      "id": "46c42263-0a67-4557-a52a-77e6a5d346a3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2480,
        608
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 704,
        "content": "## Google Sheets Data Sources\nReads your Inventory Master, supplier directory, and purchase order history to enrich Shopify data with business context and past replenishment activity.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4558360f-db6d-4228-b4ed-94a3f793e203",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1952,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 400,
        "content": "## Merge All Data\nCombines Shopify + Sheets inputs into a single SKU-level stream so calculations and routing apply consistently across all items.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6ebce15e-07df-4444-bccd-19becba39620",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1568,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 256,
        "content": "## Sales Velocity\nCalculates sales/day using 7-day and 30-day windows to capture both short-term spikes and stable demand trends.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ce3d4160-cd16-4f21-a217-3e59893aee1a",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 528,
        "content": "## Dynamic Reorder Point\nComputes a SKU-specific reorder point using velocity, lead time, and safety stock. Determines when replenishment should be triggered."
      },
      "typeVersion": 1
    },
    {
      "id": "3f38f104-8744-4cff-b325-bb36dceb7c84",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -736,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 464,
        "content": "## Stockout Risk & Critical Alerts\nScores stockout risk and notifies the team via Slack for critical SKUs that need immediate attention."
      },
      "typeVersion": 1
    },
    {
      "id": "62bd5572-d0d7-45c2-973c-842b75c6495a",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 288,
        "content": "## Warehouse Redistribution\nChecks if inventory can be shifted across warehouses to prevent stockouts before creating new purchase orders."
      },
      "typeVersion": 1
    },
    {
      "id": "277ca3e9-36bd-4f5f-8d2b-e27cbb0c66bb",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 304,
        "content": "## Supplier Checks\nEnriches SKUs with supplier data and verifies availability so only feasible items proceed to PO creation."
      },
      "typeVersion": 1
    },
    {
      "id": "a2b8eb8a-a1c8-499f-af63-735373543515",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        752
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Inventory Sync (Shopify)\nBuilds the final inventory update payload per SKU and writes the updated inventory fields back to Shopify to keep stock data consistent after reorder/redistribution decisions."
      },
      "typeVersion": 1
    },
    {
      "id": "6249c230-7252-4812-9501-702a786fff3c",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1616,
        1104
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 304,
        "content": "## Slow-Movers Detection & Actions\nIdentifies low-velocity products, structures a short recommendation list (discount/bundle/clearance), and sends Slack suggestions so the team can reduce overstock and carrying costs."
      },
      "typeVersion": 1
    },
    {
      "id": "0479b1ce-2d7d-46c7-a281-240227de5ccc",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -592,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 592,
        "content": "## Analytics Outputs & System Sync\nStructures KPI metrics (turnover, carrying cost, stockout savings) and writes them to Google Sheets dashboards and scenario planning. Also pushes summary values to an external accounting endpoint for financial tracking and reconciliation."
      },
      "typeVersion": 1
    },
    {
      "id": "b3ef9fe3-a922-473d-9fd1-4ab405b97d5f",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 704,
        "content": "## Business Rules & Approval Gates\nApplies operational constraints before creating a purchase order: business-day execution, MOQ compliance, promotional-period handling, budget limits, and large-order approval thresholds."
      },
      "typeVersion": 1
    },
    {
      "id": "854c6831-4a87-406d-8705-0b778ac7cf5a",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        656
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 400,
        "content": "## Order Calculation & Profit Prioritization\nCalculates adjusted order quantity/value (enforces MOQ) and ranks SKUs by profit priority using margin, velocity, carrying cost, and stockout risk to decide what to reorder first under constraints."
      },
      "typeVersion": 1
    },
    {
      "id": "10992c81-8e33-46bd-b43e-44f2891e1ed2",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -592,
        1344
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 336,
        "content": "## PO Line Items & Supplier Grouping\nStandardizes PO line items (SKU, quantity, pricing) and then groups them by supplier to generate a per-supplier purchase order with totals, PO number, and a ready-to-send email context (including formatted line items)."
      },
      "typeVersion": 1
    },
    {
      "id": "0ff6bdfb-9ad8-4a42-a7c9-f06f4d57a814",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        1072
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 704,
        "content": "## Send PO & Notify Team\nDelivers the purchase order to the supplier via email (Gmail) and/or a supplier API endpoint. After sending, posts a Slack notification with the PO number, supplier name, and total amount so the team has immediate visibility."
      },
      "typeVersion": 1
    },
    {
      "id": "ad77f7a9-b710-4183-a617-af90ff5fe9e0",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        1072
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 336,
        "content": "## PO Confirmation, Logging & Daily Summary\nAfter a PO is sent, waits for a confirmation checkpoint, then appends/updates the Purchase Order Log in Google Sheets. Aggregates daily operational totals (PO count/value, critical alerts, slow-movers) with safe fallbacks and posts a concise daily summary to Slack."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "991ff574-2fe9-4b14-b71b-62c8b01366bd",
  "connections": {
    "Check MOQ Met": {
      "main": [
        [
          {
            "node": "Check Promotional Period",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send PO Email": {
      "main": [
        [
          {
            "node": "Wait for PO Confirmation",
            "type": "main",
            "index": 0
          },
          {
            "node": "Alert PO Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hourly Trigger": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Suppliers": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Check Budget Limit": {
      "main": [
        [
          {
            "node": "Check Large Order Approval Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Business Day": {
      "main": [
        [
          {
            "node": "Check MOQ Met",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Detect Slow-Movers": {
      "main": [
        [
          {
            "node": "Structure Slow-Mover Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Product Details": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Inventory Levels": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Inventory Master": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Merge All Data Sources": {
      "main": [
        [
          {
            "node": "Calculate Sales Velocity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Get Inventory Levels",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Product Details",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Last 30 Days Orders",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Suppliers",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Purchase Order Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Inventory Master",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Daily Summary": {
      "main": [
        [
          {
            "node": "Send Daily Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Stockout Risk": {
      "main": [
        [
          {
            "node": "Check High Stockout Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Last 30 Days Orders": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Read Purchase Order Log": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "Structure PO Line Items": {
      "main": [
        [
          {
            "node": "Prepare PO Email Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Sales Velocity": {
      "main": [
        [
          {
            "node": "Calculate Dynamic Reorder Point",
            "type": "main",
            "index": 0
          },
          {
            "node": "Detect Slow-Movers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check High Stockout Risk": {
      "main": [
        [
          {
            "node": "Check Warehouse Redistribution Possible",
            "type": "main",
            "index": 0
          },
          {
            "node": "Alert Critical Stock Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Promotional Period": {
      "main": [
        [
          {
            "node": "Check Budget Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Optimize Profit Priority": {
      "main": [
        [
          {
            "node": "Structure PO Line Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare PO Email Context": {
      "main": [
        [
          {
            "node": "Send PO Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send PO to Supplier API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Analytics Data": {
      "main": [
        [
          {
            "node": "Write Dashboard Metrics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Write Scenario Planning",
            "type": "main",
            "index": 0
          },
          {
            "node": "Sync to Accounting System",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for PO Confirmation": {
      "main": [
        [
          {
            "node": "Update Purchase Order Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enrich with Supplier Data": {
      "main": [
        [
          {
            "node": "Check Business Day",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Slow-Mover Data": {
      "main": [
        [
          {
            "node": "Alert Slow-Mover Suggestions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Purchase Order Log": {
      "main": [
        [
          {
            "node": "Aggregate Daily Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Reorder Point Reached": {
      "main": [
        [
          {
            "node": "Calculate Stockout Risk",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Structure Analytics Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Supplier Availability": {
      "main": [
        [
          {
            "node": "Enrich with Supplier Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Inventory Updates": {
      "main": [
        [
          {
            "node": "Update Inventory Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Dynamic Reorder Point": {
      "main": [
        [
          {
            "node": "Check Reorder Point Reached",
            "type": "main",
            "index": 0
          },
          {
            "node": "Structure Inventory Updates",
            "type": "main",
            "index": 0
          },
          {
            "node": "Structure Analytics Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Large Order Approval Needed": {
      "main": [
        [
          {
            "node": "Calculate Order Quantity and Value",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Order Quantity and Value": {
      "main": [
        [
          {
            "node": "Optimize Profit Priority",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Multi-Warehouse Distribution Logic": {
      "main": [
        [
          {
            "node": "Structure Transfer Recommendations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Transfer Recommendations": {
      "main": [
        [
          {
            "node": "Alert Critical Stock Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Warehouse Redistribution Possible": {
      "main": [
        [
          {
            "node": "Multi-Warehouse Distribution Logic",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Supplier Availability",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}