{
  "name": "Tool - generate_excel_summary (V3)",
  "nodes": [
    {
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "chat_id"
            },
            {
              "name": "items"
            },
            {
              "name": "usage_count"
            },
            {
              "name": "usage_limit"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.2,
      "position": [
        1296,
        112
      ],
      "id": "3534320f-031b-4519-b9dd-8698dbcbe557",
      "name": "When Executed by Another Workflow"
    },
    {
      "parameters": {
        "jsCode": "// \u4eceTrigger\u62ff\u5230AI Agent\u4f20\u6765\u7684\u5546\u54c1\u6e05\u5355JSON\u5b57\u7b26\u4e32,\u89e3\u6790\u6210\u7269\u4ef6\u9635\u5217\nconst items = JSON.parse($json.items);\nconst chatId = $json.chat_id;\n\n// \u5224\u65adspec\u3001stock_qty\u8fd92\u4e2a\"\u9009\u586b\"\u6b04\u4f4d,\u662f\u4e0d\u662f\u5168\u90e8\u54c1\u9879\u90fd\u6ca1\u586b\n// cost_price\u4e0d\u5728\u8fd9\u4e2a\u5224\u65ad\u8303\u56f4\u5185\u2014\u2014\u5b83\u6c38\u8fdc\u4fdd\u7559\u680f\u4f4d,\u6ca1\u503c\u5c31\u81ea\u52a8\u88650\nconst hasAnyValue = (key) => items.some(item =>\n  item[key] !== null && item[key] !== undefined && item[key] !== ''\n);\n\nconst hasSpec = hasAnyValue('spec');\nconst hasStockQty = hasAnyValue('stock_qty');\n\n// \u524d\u6bb5\u6b04\u4f4d\u6e05\u5355,\u987a\u5e8f\u5b8c\u5168\u7167\u5b98\u65b9\u5546\u54c1\u5bfc\u5165\u6a21\u677f\u8d70\n// \u6761\u7801/\u6269\u5c55\u6761\u7801/\u8d77\u552e\u91cf/\u4e3b\u5355\u4f4d\u8fd94\u680f\u4e0d\u5728items schema\u7ba1\u7684\u8303\u56f4\u5185,\n// \u56fa\u5b9a\u5f53\u7a7a\u767d\u5360\u4f4d\u680f\u7559\u7740,\u4fdd\u6301\u6a21\u677f\u539f\u6709\u680f\u4f4d\u4e0d\u7f3a\u6f0f\nconst frontColumns = [\n  { key: 'name', header: '\u540d\u79f0\uff08\u5fc5\u586b\uff09' },\n  { key: 'category', header: '\u5206\u7c7b\uff08\u5fc5\u586b\uff09' },\n  { key: '_placeholder', header: '\u6761\u7801' },\n  { key: '_placeholder', header: '\u6269\u5c55\u6761\u7801' },\n  ...(hasSpec ? [{ key: 'spec', header: '\u89c4\u683c' }] : []),\n  { key: '_placeholder', header: '\u8d77\u552e\u91cf' },\n  { key: '_placeholder', header: '\u4e3b\u5355\u4f4d' },\n  ...(hasStockQty ? [{ key: 'stock_qty', header: '\u5e93\u5b58\u91cf' }] : []),\n  { key: 'cost_price', header: '\u8fdb\u8d27\u4ef7\uff08\u5fc5\u586b\uff09' },\n  { key: 'sell_price', header: '\u9500\u552e\u4ef7\uff08\u5fc5\u586b\uff09' },\n];\n\n// \u540e\u6bb525\u680f,\u7167\u5b98\u65b9\u6a21\u677f\u539f\u6837\u5217\u51fa,\u5404\u81ea\u914d\u4e00\u4e2aitem\u680f\u4f4dkey\u2014\u2014\n// \u76ee\u524ditems\u4ece\u6765\u4e0d\u4f1a\u5e26\u8fd9\u4e9bkey,\u6240\u4ee5\u73b0\u5728\u4f1a\u5168\u90e8\u88ab\u4e0b\u9762\u7684\u8fc7\u6ee4\u5668\u7701\u7565\u6389;\n// \u672a\u6765\u5982\u679cAI\u5f00\u59cb\u5224\u65ad\u8981\u4e0d\u8981\u81ea\u52a8\u586b\u8fd9\u4e9b\u9009\u586b\u680f\u4f4d\u3001item\u5e26\u4e0a\u5bf9\u5e94\u7684\u503c,\n// \u8fd9\u91cc\u4f1a\u81ea\u52a8\u4fa6\u6d4b\u5230\u3001\u81ea\u52a8\u628a\u90a3\u4e00\u680f\u52a0\u56de\u6765,\u4e0d\u7528\u518d\u6539\u4ee3\u7801\nconst trailingColumns = [\n  { key: 'margin_rate', header: '\u6bdb\u5229\u7387' },\n  { key: 'wholesale_price', header: '\u6279\u53d1\u4ef7' },\n  { key: 'member_price', header: '\u4f1a\u5458\u4ef7' },\n  { key: 'member_discount', header: '\u4f1a\u5458\u6298\u6263' },\n  { key: 'point_item', header: '\u79ef\u5206\u5546\u54c1' },\n  { key: 'stock_max', header: '\u5e93\u5b58\u4e0a\u9650' },\n  { key: 'stock_min', header: '\u5e93\u5b58\u4e0b\u9650' },\n  { key: 'location', header: '\u5e93\u4f4d' },\n  { key: 'brand', header: '\u54c1\u724c' },\n  { key: 'supplier', header: '\u4f9b\u8d27\u5546' },\n  { key: 'production_date', header: '\u751f\u4ea7\u65e5\u671f' },\n  { key: 'shelf_life', header: '\u4fdd\u8d28\u671f' },\n  { key: 'pinyin_code', header: '\u62fc\u97f3\u7801' },\n  { key: 'label_print', header: '\u6807\u7b7e\u6253\u5370' },\n  { key: 'custom2', header: '\u81ea\u5b9a\u4e492' },\n  { key: 'custom3', header: '\u81ea\u5b9a\u4e493' },\n  { key: 'weight', header: '\u91cd\u91cf' },\n  { key: 'is_weighed', header: '\u662f\u5426\u79f0\u91cd' },\n  { key: 'is_scale_synced', header: '\u662f\u5426\u4f20\u79e4' },\n  { key: 'is_countable', header: '\u662f\u5426\u8ba1\u6570\u5546\u54c1' },\n  { key: 'scale_code', header: '\u79f0\u7f16\u7801' },\n  { key: 'item_status', header: '\u5546\u54c1\u72b6\u6001' },\n  { key: 'item_description', header: '\u5546\u54c1\u63cf\u8ff0' },\n  { key: 'no_stock_count', header: '\u4e0d\u8ba1\u5e93\u5b58' },\n  { key: 'creation_date', header: '\u521b\u5efa\u65e5\u671f' }\n];\n\n// \u6574\u6392\u90fd\u662fnull/\u7a7a\u5b57\u7b26\u4e32\u7684trailing\u680f\u4f4d,\u4e0d\u751f\u6210\u5230Excel\u91cc\nconst activeTrailingColumns = trailingColumns.filter(col => hasAnyValue(col.key));\n\nconst ExcelJS = require('exceljs');\nconst workbook = new ExcelJS.Workbook();\nconst ws = workbook.addWorksheet('Sheet1');\n\nlet colIndex = 1;\nfrontColumns.forEach(col => {\n  ws.getCell(1, colIndex).value = col.header;\n  colIndex++;\n});\nactiveTrailingColumns.forEach(col => {\n  ws.getCell(1, colIndex).value = col.header;\n  colIndex++;\n});\n\n// \u5199\u6bcf\u4e00\u884c\u5546\u54c1\u6570\u636e\n// cost_price\u7528 Number(val) || 0,val\u5982\u679c\u662fundefined/null/\u7a7a\u5b57\u7b26\u4e32,\n// \u8fd0\u7b97\u7ed3\u679c\u90fd\u4f1a\u662f0,\u6b63\u597d\u8fbe\u5230\"\u7528\u6237\u6ca1\u7ed9\u5c31\u81ea\u52a8\u88650\"\u7684\u6548\u679c\nitems.forEach((item, rowIdx) => {\n  const r = rowIdx + 2;\n  let c = 1;\n  frontColumns.forEach(col => {\n    if (col.key === '_placeholder') {\n      c++;\n      return;\n    }\n    let val = item[col.key];\n    if (col.key === 'sell_price' || col.key === 'cost_price') {\n      ws.getCell(r, c).value = Number(val) || 0;\n      ws.getCell(r, c).numFmt = '0.00';\n    } else if (col.key === 'stock_qty') {\n      ws.getCell(r, c).value = val ?? 0;\n    } else {\n      ws.getCell(r, c).value = val || '';\n    }\n    c++;\n  });\n  activeTrailingColumns.forEach(col => {\n    const val = item[col.key];\n    ws.getCell(r, c).value = (val !== null && val !== undefined) ? val : '';\n    c++;\n  });\n});\n\nconst buffer = await workbook.xlsx.writeBuffer();\nconst base64 = buffer.toString('base64');\n\n// \u6863\u540d\u7528\u5e74\u6708\u65e5\u65f6\u5206\u79d2\u5f53\u65f6\u95f4\u6233,\u907f\u514d\u649e\u540d\nconst now = new Date();\nconst pad = (n) => String(n).padStart(2, '0');\nconst timestamp = `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;\n\nreturn [{\n  json: {\n    chat_id: chatId,\n    itemCount: items.length,\n    usage_count: $json.usage_count,\n    usage_limit: $json.usage_limit\n  },\n  binary: {\n    file: {\n      data: base64,\n      mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n      fileName: `menu_import_${timestamp}.xlsx`\n    }\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1616,
        112
      ],
      "id": "8c2df2b5-fbd7-4d9a-9eff-015591d3f795",
      "name": "\u751f\u6210\u5546\u54c1\u5bfc\u5165\u8868"
    },
    {
      "parameters": {
        "operation": "sendDocument",
        "chatId": "={{ $json.chat_id }}",
        "binaryData": true,
        "binaryPropertyName": "file",
        "additionalFields": {
          "caption": "=\u2705 \u5df2\u7ecf\u4e3a\u60a8\u6574\u7406\u597d\u83dc\u5355,\u5e76\u751f\u6210\u4e86\u5bf9\u5e94\u7684\u5bfc\u5165\u6587\u4ef6\u3002\u672c\u6b21\u6574\u7406\u5171\u5305\u542b {{ $json.itemCount }} \u4e2a\u5546\u54c1,\u8bf7\u95ee\u8fd8\u9700\u8981\u6dfb\u52a0\u5176\u4ed6\u5546\u54c1\u5417\uff1f\u6216\u8005\u9700\u8981\u6211\u4e3a\u60a8\u751f\u6210POS\u5bfc\u5165\u6a21\u677f\u5417\uff1f\n\n\ud83d\udcca \u4eca\u65e5AI\u5904\u7406\u6b21\u6570\uff1a{{ $json.usage_count }}/{{ $json.usage_limit }}",
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1824,
        112
      ],
      "id": "1447a4bb-b992-4f7a-9c58-9973be39fbd3",
      "name": "Send a document",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('\u751f\u6210\u5546\u54c1\u5bfc\u5165\u8868').item.json.chat_id }}",
        "text": "\u8fd9\u6b21\u7684\u8bc6\u522b\u6548\u679c\u600e\u4e48\u6837\uff1f\u6709\u4ec0\u4e48\u60f3\u8ba9\u6211\u6539\u8fdb\u7684\u53ef\u4ee5\u76f4\u63a5\u6253\u5b57\u544a\u8bc9\u6211 \ud83d\ude4f",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2b50",
                    "additionalFields": {
                      "callback_data": "rate_1"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2b50\u2b50",
                    "additionalFields": {
                      "callback_data": "rate_2"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2b50\u2b50\u2b50",
                    "additionalFields": {
                      "callback_data": "rate_3"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2b50\u2b50\u2b50\u2b50",
                    "additionalFields": {
                      "callback_data": "rate_4"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2b50\u2b50\u2b50\u2b50\u2b50",
                    "additionalFields": {
                      "callback_data": "rate_5"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udd27 \u9700\u8981\u4fee\u6539",
                    "additionalFields": {
                      "callback_data": "modify"
                    }
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2705 \u7ed3\u675f\u8fd0\u884c",
                    "additionalFields": {
                      "callback_data": "end_modify"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2032,
        112
      ],
      "id": "1d403650-46ad-4f42-92a7-048f87b8a777",
      "name": "Send a text message",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "\u751f\u6210\u5546\u54c1\u5bfc\u5165\u8868",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u751f\u6210\u5546\u54c1\u5bfc\u5165\u8868": {
      "main": [
        [
          {
            "node": "Send a document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a document": {
      "main": [
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "7af51ab1-9e1c-4cd9-88c7-7bc4ef8fcd17",
  "nodeGroups": [],
  "id": "5m7xS062vlea0Cv3",
  "tags": [
    {
      "updatedAt": "2026-07-27T07:50:01.682Z",
      "createdAt": "2026-07-27T07:50:01.682Z",
      "id": "oulTjbe8ViVdh1V7",
      "name": "AUTO-P1-V3"
    },
    {
      "updatedAt": "2026-07-20T07:40:54.445Z",
      "createdAt": "2026-07-20T07:40:54.445Z",
      "id": "sBao71AvdSJ3hx2O",
      "name": "3FS"
    }
  ]
}