GLPIサポートパフォーマンスレポートの生成(SLA追跡+メール送信付き)
上級
これはContent Creation, Multimodal AI分野の自動化ワークフローで、20個のノードを含みます。主にSet, Code, Html, Gmail, SplitOutなどのノードを使用。 GLPIサポートパフォーマンスレポートを自動生成し、SLAモニタリングとメール送信機能を含める
前提条件
- •Googleアカウント + Gmail API認証情報
- •ターゲットAPIの認証情報が必要な場合あり
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
"meta": {
"instanceId": "6fe840c9f744c9e44a1c50b6467124995157353b1fc80f3a8dd173276e4fc270",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "d17beade-5e8d-482f-ad6a-d3f692b3ddc0",
"name": "Variables",
"type": "n8n-nodes-base.set",
"position": [
-448,
32
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ea290016-88f0-4287-be92-7a3f8c7046d6",
"name": "Server URL",
"type": "string",
"value": "https://server_glpi.com/"
},
{
"id": "5cd7f69c-f079-4155-b887-c4b5e6e1cb64",
"name": "App Token",
"type": "string",
"value": "\"You app token\""
},
{
"id": "4c2a3325-1547-4333-805a-c74b711e3bc5",
"name": "Entity name",
"type": "string",
"value": "\"name_entity\""
},
{
"id": "96765e53-2275-41e9-a297-b64207fa4a79",
"name": "WORK_START",
"type": "string",
"value": "8"
},
{
"id": "4c04dfd8-64ef-4ae0-8375-e3108222a4b8",
"name": "LUNCH_START",
"type": "string",
"value": "12"
},
{
"id": "b76f06aa-405f-421d-b64f-7468222ddfb8",
"name": "LUNCH_END",
"type": "string",
"value": "13"
},
{
"id": "1a1a5672-226e-439e-8121-bb48fbe46f67",
"name": "WORK_END",
"type": "string",
"value": "18"
},
{
"id": "b8ee7859-6533-4642-861b-d0e0136c3b39",
"name": "SLA_LIMIT_HOURS",
"type": "string",
"value": "24"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "3042ce97-b7b8-47c0-b702-905a31114297",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
416,
32
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "5a116716-658c-425a-9817-b84cfa462819",
"name": "Ticket_id",
"type": "string",
"value": "={{ $json[\"2\"] }}"
},
{
"id": "6deaea47-3c4a-4a2f-80de-a790f4dcd417",
"name": "Title",
"type": "string",
"value": "={{ $json[\"1\"] }}"
},
{
"id": "a710ff8c-c898-435d-91eb-c67e71ddc5f7",
"name": "technical_id",
"type": "string",
"value": "={{ $json[\"5\"] }}"
},
{
"id": "0ee2215d-760e-433e-9f1f-3237a8d0860c",
"name": "Entity",
"type": "string",
"value": "={{ $json[\"80\"] }}"
},
{
"id": "7b02dcbd-49df-43f9-b4e4-01b7d93a9a8e",
"name": "Opening date",
"type": "string",
"value": "={{ $json[\"15\"] }}"
},
{
"id": "62e29ecb-0dc2-4e94-ada5-c9e076dc596d",
"name": "Solution date",
"type": "string",
"value": "={{ $json[\"17\"] }}"
},
{
"id": "9741e21c-b42a-42e7-a58b-68947b24f29e",
"name": "Closing date",
"type": "string",
"value": "={{ $json[\"16\"] }}"
},
{
"id": "06abb1e0-7d26-4ac1-bd5e-a1c3b1823e3e",
"name": "Status",
"type": "string",
"value": "={{ $json[\"12\"] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "7cf4932f-9afd-4fb4-94e7-22f2f7c634a9",
"name": "分割出力",
"type": "n8n-nodes-base.splitOut",
"position": [
192,
32
],
"parameters": {
"options": {},
"fieldToSplitOut": "data"
},
"typeVersion": 1
},
{
"id": "e583f78f-0288-4166-b930-ebb39a88f7ef",
"name": "Send a message",
"type": "n8n-nodes-base.gmail",
"position": [
1056,
-176
],
"webhookId": "4bee6d0c-8e87-46ff-90f0-a3539439b8cf",
"parameters": {
"sendTo": "info@example.com",
"message": "={{ $json.html }}",
"options": {},
"subject": "=Report - {{ $('Date range').first().json.month }}"
},
"credentials": {
"gmailOAuth2": {
"id": "1vGUy7BPigbNRllM",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "0cd4a9c1-5e1e-47b8-ba4c-92ffbdfef6d2",
"name": "Get tickets",
"type": "n8n-nodes-base.httpRequest",
"position": [
-16,
-176
],
"parameters": {
"url": "={{ $('Variables').item.json[\"Server URL\"] }}apirest.php/search/Ticket",
"options": {},
"sendQuery": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"queryParameters": {
"parameters": [
{
"name": "criteria[0][field]",
"value": "15"
},
{
"name": "criteria[0][searchtype]",
"value": "morethan"
},
{
"name": "criteria[0][value]",
"value": "={{ $('Date range').item.json.startDate }}"
},
{
"name": "criteria[1][link]",
"value": "AND"
},
{
"name": "criteria[1][field]",
"value": "15"
},
{
"name": "criteria[1][searchtype]",
"value": "lessthan"
},
{
"name": "criteria[1][value]",
"value": "={{ $('Date range').item.json.endDate }}"
},
{
"name": "criteria[2][link]",
"value": "AND"
},
{
"name": "criteria[2][field]",
"value": "80"
},
{
"name": "criteria[2][searchtype]",
"value": "contains"
},
{
"name": "criteria[2][value]",
"value": "={{ $('Variables').item.json[\"Entity name\"] }}"
},
{
"name": "order",
"value": "DESC"
},
{
"name": "range",
"value": "0-999"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Session-Token",
"value": "={{ $json.session_token }}"
},
{
"name": "App-Token",
"value": "={{ $('Variables').item.json[\"App Token\"] }}"
}
]
}
},
"credentials": {
"httpBasicAuth": {
"id": "1ri8MpwPvPOW9M8n",
"name": "Api GLPI"
}
},
"typeVersion": 4.2
},
{
"id": "0dc82824-be50-485d-b49a-398fff0dd4e1",
"name": "スケジュールトリガー",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-464,
-544
],
"parameters": {
"rule": {
"interval": [
{
"field": "months",
"triggerAtDayOfMonth": 6
}
]
}
},
"typeVersion": 1.2
},
{
"id": "3b5061f5-6fb0-4620-8d1d-d699f973e755",
"name": "Get session token",
"type": "n8n-nodes-base.httpRequest",
"position": [
-208,
32
],
"parameters": {
"url": "={{ $json[\"Server URL\"] }}apirest.php/initSession",
"options": {},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "App-Token",
"value": "={{ $json[\"App Token\"] }}"
}
]
}
},
"credentials": {
"httpBasicAuth": {
"id": "1ri8MpwPvPOW9M8n",
"name": "Api GLPI"
}
},
"typeVersion": 4.2
},
{
"id": "06ea7bb1-1347-4e14-8499-8df9c4b0619b",
"name": "End session",
"type": "n8n-nodes-base.httpRequest",
"position": [
1232,
-176
],
"parameters": {
"url": "={{ $('Variables').first().json[\"Server URL\"] }}apirest.php/killSession",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Session-Token",
"value": "={{ $('Get session token').first().json.session_token }}"
},
{
"name": "App-Token",
"value": "={{ $('Variables').first().json[\"App Token\"] }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "5af4ce0e-c957-49a0-a081-7e5c9b84a714",
"name": "操作なし, do nothing",
"type": "n8n-nodes-base.noOp",
"position": [
1408,
-176
],
"parameters": {},
"typeVersion": 1
},
{
"id": "51e2a4d5-d133-4ba7-9013-e24e10529a15",
"name": "Date range",
"type": "n8n-nodes-base.code",
"position": [
-464,
-272
],
"parameters": {
"jsCode": "// Get the date from the Schedule Trigger\nconst today = new Date($input.item.json.timestamp);\n\n// Calculate the previous month\nconst previousMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1);\n\n// Start date: last day of the month before the previous month\nconst startDate = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), 0);\n\n// End date: first day of the month after the previous month\nconst endDate = new Date(previousMonth.getFullYear(), previousMonth.getMonth() + 1, 1);\n\n// Format as YYYY-MM-DD\nconst startDateStr = startDate.toISOString().split('T')[0];\nconst endDateStr = endDate.toISOString().split('T')[0];\n\n// Get the report month and year\nconst monthNames = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December'\n];\nconst reportMonth = monthNames[previousMonth.getMonth()];\nconst reportYear = previousMonth.getFullYear();\n\nreturn {\n json: {\n month: `${reportMonth} ${reportYear}`,\n startDate: startDateStr,\n endDate: endDateStr\n }\n};"
},
"typeVersion": 2
},
{
"id": "1946b065-da80-413f-aaee-0d23ce11eb6f",
"name": "付箋",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
-720
],
"parameters": {
"color": 4,
"width": 544,
"height": 304,
"content": "## Schedule\n\nThe configuration is set to start on the 6th day of each month to ensure accurate SLA measurement. This is because if the cut-off date were set to the last day of the month, cases opened in the days immediately prior would not be measured correctly, negatively impacting the SLA calculation."
},
"typeVersion": 1
},
{
"id": "1d5b834b-be04-4c55-9282-33586fd9a6a6",
"name": "付箋1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
-400
],
"parameters": {
"width": 544,
"height": 288,
"content": "## Data range \nThe month prior to the execution month is identified, and the range covering all days of that month is determined.\n\nExample:\n\nmonth: September 2025\nstartDate: 2025-08-31\nendDate: 2025-10-01\n\ndays\": 30,\ndates\": [\"2025-09-01\", \"...\", \"2025-09-30\"]\n"
},
"typeVersion": 1
},
{
"id": "1284666f-52b4-49c0-b398-5e028eb80823",
"name": "付箋2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
-96
],
"parameters": {
"color": 3,
"width": 544,
"height": 400,
"content": "## Variables\n\nIn this node, the connection parameters to the GLPI server are updated:\n\nGLPI server URL: https://server_glpi.com/\n\nGLPI API App-Token: Here_App-Token\n\nGLPI entity name: name_entity\n\n\n\nSetting working hours \n\n(WORK_START, LUNCH_START, LUNCH_END, WORK_END) and SLA limit in hours (SLA_LIMIT_HOURS)"
},
"typeVersion": 1
},
{
"id": "9ec74986-a8f0-43ec-935b-e26f16e45376",
"name": "付箋4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
-368
],
"parameters": {
"color": 5,
"width": 480,
"height": 320,
"content": "## Get tickets\n\nThe node retrieves GLPI cases based on the defined entity, applying the date range configured in Data range to filter by opening date. By default, the query is limited to a range of 0–999 cases, which can be adjusted as needed."
},
"typeVersion": 1
},
{
"id": "f38ad03a-8f49-4169-94da-ae3765796acc",
"name": "付箋3",
"type": "n8n-nodes-base.stickyNote",
"position": [
256,
-576
],
"parameters": {
"width": 544,
"height": 528,
"content": "## Get report\nhe node calculates the effective working hours between ticket opening and resolution/closure, considering only business hours (8:00–12:00 and 13:00–16:00, excluding weekends).\n\nChecks SLA compliance based on SLA_LIMIT_HOURS (default: 24 hours).\n\n- Classifies tickets by status (open, in progress, resolved, closed).\n- Sums total working hours and calculates the average hours per ticket.\n- Determines the SLA compliance percentage.\n\n\n### Generates two reports:\n- General Report: metrics for all incidents.\n- Report by Technician: metrics per technician.\n\n"
},
"typeVersion": 1
},
{
"id": "15df2869-8142-46b3-98f5-ef58bb750794",
"name": "Metrics",
"type": "n8n-nodes-base.code",
"position": [
544,
-176
],
"parameters": {
"jsCode": "// n8n Function Node - GLPI Report with Case Totals\n// =================================================\n\n// Business hours configuration\nconst WORK_START = $('Variables').first().json.WORK_START; // 8 AM\nconst LUNCH_START = $('Variables').first().json.LUNCH_START; // 12 PM\nconst LUNCH_END = $('Variables').first().json.WORK_END; // 1 PM\nconst WORK_END = $('Variables').first().json.WORK_END; // 6 PM\n\n// SLA configuration\nconst SLA_LIMIT_HOURS = $('Variables').first().json.SLA_LIMIT_HOURS; // cambiar aquí según SLA\n\nfunction toDate(str) {\n return str ? new Date(str.replace(\" \", \"T\")) : null;\n}\n\nfunction isWeekend(date) {\n const day = date.getDay();\n return day === 0 || day === 6;\n}\n\n// Calculate effective working hours within business schedule\nfunction businessHoursDiff(start, end) {\n if (!start || !end) return 0;\n if (end < start) return 0;\n\n let totalHours = 0;\n let current = new Date(start);\n\n while (current < end) {\n if (isWeekend(current)) {\n current.setDate(current.getDate() + 1);\n current.setHours(WORK_START, 0, 0, 0);\n continue;\n }\n\n const workMorningStart = new Date(current.setHours(WORK_START, 0, 0, 0));\n const workMorningEnd = new Date(current.setHours(LUNCH_START, 0, 0, 0));\n const workAfternoonStart = new Date(current.setHours(LUNCH_END, 0, 0, 0));\n const workAfternoonEnd = new Date(current.setHours(WORK_END, 0, 0, 0));\n\n const dayStart = start > workMorningStart ? start : workMorningStart;\n const dayEnd = end < workAfternoonEnd ? end : workAfternoonEnd;\n\n if (dayStart < workMorningEnd) {\n totalHours += Math.max(0, (Math.min(dayEnd, workMorningEnd) - dayStart) / 3600000);\n }\n if (dayEnd > workAfternoonStart) {\n totalHours += Math.max(0, (dayEnd - Math.max(dayStart, workAfternoonStart)) / 3600000);\n }\n\n current.setDate(current.getDate() + 1);\n current.setHours(WORK_START, 0, 0, 0);\n }\n\n return totalHours;\n}\n\n// Convert decimal → \"Xh Ym\"\nfunction decimalToHM(decimal) {\n const hours = Math.floor(decimal);\n const minutes = Math.round((decimal - hours) * 60);\n return `${hours}h ${minutes}m`;\n}\n\n// Initialize metrics\nlet general = { open: 0, in_progress: 0, solved: 0, closed: 0, hours: 0, sla_ok: 0, total: 0 };\nlet byTechnician = {};\n\n// Process tickets\nfor (const item of items) {\n const t = item.json;\n\n const opening = toDate(t[\"Opening date\"]);\n const solution = toDate(t[\"Solution date\"]);\n const closing = toDate(t[\"Closing date\"]);\n const technician = t.technical_id || \"Unassigned\";\n const status = parseInt(t.Status, 10);\n\n const end = solution || closing;\n const hours = end ? businessHoursDiff(opening, end) : 0;\n const sla_ok = solution ? (hours <= SLA_LIMIT_HOURS ? 1 : 0) : 0;\n\n // General report\n if (status === 1) general.open++;\n if (status === 2) general.in_progress++;\n if (status === 5) general.solved++;\n if (status === 6) general.closed++;\n general.hours += hours;\n general.sla_ok += sla_ok;\n general.total++;\n\n // Report by technician\n if (!byTechnician[technician]) {\n byTechnician[technician] = { open: 0, in_progress: 0, solved: 0, closed: 0, hours: 0, sla_ok: 0, total: 0 };\n }\n if (status === 1) byTechnician[technician].open++;\n if (status === 2) byTechnician[technician].in_progress++;\n if (status === 5) byTechnician[technician].solved++;\n if (status === 6) byTechnician[technician].closed++;\n byTechnician[technician].hours += hours;\n byTechnician[technician].sla_ok += sla_ok;\n byTechnician[technician].total++;\n}\n\n// Function to build summary\nfunction summary(data) {\n return {\n \"Total cases\": data.total,\n \"Open cases\": data.open,\n \"Cases in progress\": data.in_progress,\n \"Solved cases\": data.solved,\n \"Closed cases\": data.closed,\n \"Total hours (decimal)\": data.hours.toFixed(2),\n \"Total hours (formatted)\": decimalToHM(data.hours),\n \"Average hours (decimal)\": data.total > 0 ? (data.hours / data.total).toFixed(2) : 0,\n \"Average hours (formatted)\": data.total > 0 ? decimalToHM(data.hours / data.total) : \"0h 0m\",\n \"SLA compliance\": data.total > 0 ? ((data.sla_ok / data.total) * 100).toFixed(2) + \"%\" : \"0%\"\n };\n}\n\n// Final output\nreturn [\n {\n json: {\n \"General Report\": summary(general),\n \"Report by Technician\": Object.fromEntries(\n Object.entries(byTechnician).map(([tech, data]) => [tech, summary(data)])\n )\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "4dc2fc78-aedc-44ac-9c12-8343dee0ef20",
"name": "Generate report",
"type": "n8n-nodes-base.html",
"position": [
880,
-176
],
"parameters": {
"html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>Case Report - Technical Support</title>\n <style>\n /* Reset styles */\n body { margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }\n table { border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }\n img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; }\n \n /* Responsive styles */\n @media only screen and (max-width: 620px) {\n .wrapper { width: 100% !important; }\n .container { width: 100% !important; min-width: 100% !important; }\n .mobile-padding { padding-left: 15px !important; padding-right: 15px !important; }\n .metric-card { display: block !important; width: 100% !important; margin-bottom: 10px !important; }\n .metric-row td { display: block !important; width: 100% !important; padding-bottom: 10px !important; }\n .hide-mobile { display: none !important; }\n .table-responsive { font-size: 12px !important; }\n .table-responsive td { padding: 8px 4px !important; }\n }\n </style>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; background-color: #f3f4f6;\">\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"background-color: #f3f4f6; padding: 20px;\" class=\"wrapper\">\n <tr>\n <td align=\"center\">\n <table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"background-color: #ffffff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); max-width: 600px;\" class=\"container\">\n \n <!-- Header -->\n <tr>\n <td style=\"background: linear-gradient(135deg, #2F3F64 0%, #1a2744 100%); background-color: #2F3F64; padding: 40px 30px; text-align: center;\" class=\"mobile-padding\">\n <h1 style=\"margin: 0; color: #ffffff; font-size: 26px; font-weight: bold; line-height: 1.3;\">\n 📊 Technical Support Case Report - {{ $('Date range').first().json.month }}\n </h1>\n <p style=\"margin: 8px 0 0 0; color: #e2e8f0; font-size: 14px;\">\n Complete analysis of performance and case status\n </p>\n </td>\n </tr>\n \n <!-- Content -->\n <tr>\n <td style=\"padding: 30px;\" class=\"mobile-padding\">\n \n <!-- Section Title -->\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n <tr>\n <td style=\"border-left: 4px solid #2F3F64; padding-left: 12px;\">\n <h2 style=\"margin: 0; font-size: 18px; color: #0f172a; font-weight: bold;\">\n 📈 General Summary\n </h2>\n </td>\n </tr>\n </table>\n \n <!-- Metrics Row 1 -->\n <table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 12px;\" class=\"metric-row\">\n <tr>\n <!-- Total Cases -->\n <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #3b82f6; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n TOTAL CASES\n </div>\n <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Total cases\"]}}\n </div>\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- Open Cases -->\n <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #f59e0b; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n OPEN CASES\n </div>\n <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Open cases\"]}}\n </div>\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- In Progress with Alert -->\n <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #8b5cf6; border-radius: 8px; padding: 16px; vertical-align: top; position: relative;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n IN PROGRESS\n </div>\n <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Cases in progress\"]}}\n </div>\n {{parseInt($json[\"General Report\"][\"Cases in progress\"]) > 100 ? `\n <div style=\"margin-top: 8px; padding: 4px 8px; background-color: #fef3c7; border-radius: 4px; font-size: 10px; color: #92400e; font-weight: bold;\">\n ⚠️ HIGH VOLUME\n </div>\n ` : ''}}\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- Resolved -->\n <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #10b981; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n RESOLVED\n </div>\n <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Solved cases\"]}}\n </div>\n </td>\n </tr>\n </table>\n \n <!-- Metrics Row 2 -->\n <table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 30px;\" class=\"metric-row\">\n <tr>\n <!-- Closed -->\n <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #6b7280; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n CLOSED\n </div>\n <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Closed cases\"]}}\n </div>\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- Total Hours -->\n <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #3b82f6; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n TOTAL HOURS\n </div>\n <div style=\"font-size: 20px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Total hours (formatted)\"]}}\n </div>\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- Average -->\n <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #f59e0b; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n AVERAGE\n </div>\n <div style=\"font-size: 20px; color: #0f172a; font-weight: bold; line-height: 1;\">\n {{$json[\"General Report\"][\"Average hours (formatted)\"]}}\n </div>\n </td>\n \n <td width=\"2%\" class=\"hide-mobile\"></td>\n \n <!-- SLA Compliance with Dynamic Color -->\n <td width=\"23%\" style=\"background-color: #f8fafc; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n {{(() => {\n const sla = parseFloat($json[\"General Report\"][\"SLA compliance\"]);\n const borderColor = sla >= 80 ? '#10b981' : sla >= 50 ? '#f59e0b' : '#ef4444';\n return `<div style=\"border-left: 4px solid ${borderColor}; padding-left: 12px;\">\n <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n SLA COMPLIANCE\n </div>\n <div style=\"font-size: 22px; color: #0f172a; font-weight: bold; line-height: 1;\">\n ${$json[\"General Report\"][\"SLA compliance\"]}\n </div>\n </div>`;\n })()}}\n </td>\n </tr>\n </table>\n \n <!-- Alert Section for Critical SLA -->\n {{parseFloat($json[\"General Report\"][\"SLA compliance\"]) < 50 ? `\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n <tr>\n <td style=\"background-color: #fef2f2; border-left: 4px solid #dc2626; border-radius: 8px; padding: 16px;\">\n <div style=\"font-size: 14px; color: #991b1b; font-weight: bold; margin-bottom: 4px;\">\n ⚠️ CRITICAL ALERT\n </div>\n <div style=\"font-size: 13px; color: #7f1d1d; line-height: 1.5;\">\n SLA compliance is below 50%. Immediate action required to improve service levels.\n </div>\n </td>\n </tr>\n </table>\n ` : ''}}\n \n <!-- Divider -->\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin: 30px 0;\">\n <tr>\n <td style=\"border-bottom: 1px solid #e2e8f0;\"></td>\n </tr>\n </table>\n \n <!-- Section Title 2 -->\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n <tr>\n <td style=\"border-left: 4px solid #2F3F64; padding-left: 12px;\">\n <h2 style=\"margin: 0; font-size: 18px; color: #0f172a; font-weight: bold;\">\n 👥 Technician Details\n </h2>\n </td>\n </tr>\n </table>\n \n <!-- Table -->\n <table width=\"100%\" cellpadding=\"12\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden;\" class=\"table-responsive\">\n <thead>\n <tr style=\"background-color: #f8fafc;\">\n <th style=\"text-align: left; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Technician</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Total</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Open</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Progress</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Solved</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Closed</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Total Hrs</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Average</th>\n <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">SLA</th>\n </tr>\n </thead>\n <tbody>\n {{($json[\"Report by Technician\"] && Object.keys($json[\"Report by Technician\"]).length > 0) \n ? Object.keys($json[\"Report by Technician\"]).map(techId => {\n const tech = $json[\"Report by Technician\"][techId];\n const sla = parseFloat(tech[\"SLA compliance\"]);\n const bgColor = sla >= 80 ? '#d1fae5' : sla >= 50 ? '#fef3c7' : '#fee2e2';\n const textColor = sla >= 80 ? '#065f46' : sla >= 50 ? '#92400e' : '#991b1b';\n const inProgress = parseInt(tech[\"Cases in progress\"]);\n const progressAlert = inProgress > 100 ? 'background-color: #fef3c7;' : '';\n \n return `\n <tr style=\"${progressAlert}\">\n <td style=\"padding: 12px; font-size: 14px; color: #0f172a; font-weight: bold; border-bottom: 1px solid #f1f5f9;\">\n Technician #${techId}\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n ${tech[\"Total cases\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n ${tech[\"Open cases\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n <span style=\"${inProgress > 100 ? 'font-weight: bold; color: #92400e;' : 'color: #334155;'}\">\n ${tech[\"Cases in progress\"]}${inProgress > 100 ? ' ⚠️' : ''}\n </span>\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n ${tech[\"Solved cases\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n ${tech[\"Closed cases\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n ${tech[\"Total hours (formatted)\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n ${tech[\"Average hours (formatted)\"]}\n </td>\n <td style=\"padding: 12px; font-size: 14px; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n <span style=\"display: inline-block; padding: 6px 12px; background-color: ${bgColor}; color: ${textColor}; font-size: 12px; font-weight: bold; border-radius: 12px; white-space: nowrap;\">\n ${tech[\"SLA compliance\"]}\n </span>\n </td>\n </tr>\n `;\n }).join('')\n : '<tr><td colspan=\"9\" style=\"padding: 30px; text-align: center; color: #64748b; font-style: italic; border-bottom: 1px solid #f1f5f9;\">No technician data available</td></tr>'\n }}\n </tbody>\n </table>\n \n </td>\n </tr>\n \n <!-- Footer -->\n <tr>\n <td style=\"background-color: #f8fafc; padding: 20px; text-align: center; border-top: 1px solid #e2e8f0;\">\n <p style=\"margin: 0 0 8px 0; color: #64748b; font-size: 12px;\">\n Report automatically generated by the technical support system\n </p>\n <p style=\"margin: 0; color: #94a3b8; font-size: 11px;\">\n Generated on: {{new Date().toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: 'America/Bogota' })}}\n </p>\n </td>\n </tr>\n \n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>"
},
"typeVersion": 1.2
},
{
"id": "b8578b10-7ce4-4bcc-945b-b938cfbd2fb7",
"name": "付箋5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-368,
-1280
],
"parameters": {
"color": 7,
"width": 480,
"height": 304,
"content": "## Create GLPI API Credentials in n8n\n## Node https request\nSet up your GLPI credentials before configuring the workflow:\nCredential Name: Api GLPI (or your preferred name)\n\nAuthentication: Generic Credential Type → Basic Auth\nGeneric Auth Type: Basic Auth\nUsername: Your GLPI API username\nPassword: Your GLPI API password"
},
"typeVersion": 1
},
{
"id": "dcfed78e-cb52-4a20-87b9-41e1699743b8",
"name": "付箋6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
-1280
],
"parameters": {
"color": 7,
"width": 480,
"height": 304,
"content": "## Tickets panel GLPI\nConfigure the tickets module in Assistance > Tickets with the following items using the tool icon.\n\nID\nTitle\nStatus\nOpening Date\nClosing Date\nResolution Date\nPriority\nRequester - Requester\nAssigned To - Technician"
},
"typeVersion": 1
},
{
"id": "5623a271-7c94-40cb-9043-b9e6c2f35c71",
"name": "付箋7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1008,
-320
],
"parameters": {
"width": 192,
"height": 128,
"content": "## Email\nAdd your email account"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"15df2869-8142-46b3-98f5-ef58bb750794": {
"main": [
[
{
"node": "4dc2fc78-aedc-44ac-9c12-8343dee0ef20",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "3042ce97-b7b8-47c0-b702-905a31114297",
"type": "main",
"index": 0
}
]
]
},
"d17beade-5e8d-482f-ad6a-d3f692b3ddc0": {
"main": [
[
{
"node": "3b5061f5-6fb0-4620-8d1d-d699f973e755",
"type": "main",
"index": 0
}
]
]
},
"51e2a4d5-d133-4ba7-9013-e24e10529a15": {
"main": [
[
{
"node": "d17beade-5e8d-482f-ad6a-d3f692b3ddc0",
"type": "main",
"index": 0
}
]
]
},
"3042ce97-b7b8-47c0-b702-905a31114297": {
"main": [
[
{
"node": "15df2869-8142-46b3-98f5-ef58bb750794",
"type": "main",
"index": 0
}
]
]
},
"06ea7bb1-1347-4e14-8499-8df9c4b0619b": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"0cd4a9c1-5e1e-47b8-ba4c-92ffbdfef6d2": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"e583f78f-0288-4166-b930-ebb39a88f7ef": {
"main": [
[
{
"node": "06ea7bb1-1347-4e14-8499-8df9c4b0619b",
"type": "main",
"index": 0
}
]
]
},
"4dc2fc78-aedc-44ac-9c12-8343dee0ef20": {
"main": [
[
{
"node": "e583f78f-0288-4166-b930-ebb39a88f7ef",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "51e2a4d5-d133-4ba7-9013-e24e10529a15",
"type": "main",
"index": 0
}
]
]
},
"3b5061f5-6fb0-4620-8d1d-d699f973e755": {
"main": [
[
{
"node": "0cd4a9c1-5e1e-47b8-ba4c-92ffbdfef6d2",
"type": "main",
"index": 0
}
]
]
}
}
}よくある質問
このワークフローの使い方は?
上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。
このワークフローはどんな場面に適していますか?
上級 - コンテンツ作成, マルチモーダルAI
有料ですか?
このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。
関連ワークフロー
Groq、Gemini、Slack承認システムを使用してRSSからMediumへの公開を自動化
Groq、Gemini、Slack承認システムを用いたRSSからMediumへの自動公開プロセス
If
Set
Code
+
If
Set
Code
41 ノードObisDev
コンテンツ作成
キーワードからGPT-5とfal.ai画像を使ってWordPressまで自動SEOブログ生成のプロセス
GPT-5とfal.ai画像を使用したキーワードからWordPressへのSEOブログ自動化プロセス
Set
Code
Wait
+
Set
Code
Wait
96 ノードPaul
コンテンツ作成
コンテンツ集約
Gemini AIを使ってウェブ記事からLinkedInとX/Twitterへのソーシャルメディア投稿を自動化する
If
Set
Xml
+
If
Set
Xml
34 ノードVadim
コンテンツ作成
GLPIチケット締切り提醒の自動化
Microsoft Teamsを通じたオートメーションGLPIチケット締切提醒
If
Set
Split Out
+
If
Set
Split Out
16 ノードLuis Hernandez
チケット管理
詐欺防止型リードキャプチャ・育成千リードシステム
AIによるスコアリング、テーブル追跡、マルチチャネルアラートを使って詐欺対策リードをキャプチャーし育成
If
Set
Code
+
If
Set
Code
28 ノードJitesh Dugar
コンテンツ作成
私のワークフロー5
ジョン
Set
Code
Html
+
Set
Code
Html
102 ノードHichul
コンテンツ作成
ワークフロー情報
難易度
上級
ノード数20
カテゴリー2
ノードタイプ9
作成者
Luis Hernandez
@integropenI like solving problems and have a true passion for no-code automation. I'm thoughtful by nature and enjoy finding ways to optimize processes, always guided by my principles and values.
外部リンク
n8n.ioで表示 →
このワークフローを共有