{
  "id": "RRgZgaqK8c05uX4J",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Resource Capacity Planning:",
  "tags": [],
  "nodes": [
    {
      "id": "73e34959-f752-4bb0-a55a-73282abadde5",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -560,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "13f023dd-3654-4e19-9e68-f6be0d8aec06",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1152,
        64
      ],
      "parameters": {
        "width": 352,
        "height": 480,
        "content": "## \ud83d\udce7 Send Over-Allocation Alert to Manager\n**Action:**  \n- Delivers urgent capacity alert email to project manager/team lead.  \n**Description:**  \n- Sends formatted alert to manager with full report details.  \n- Subject line dynamically reflects severity (number of over-allocated members).  \n- Message body includes:  \n  - Clear warning indicators  \n  - Detailed breakdown of each over-allocated team member  \n  - Recommended corrective actions  \n  - Generation timestamp for reference  \n- Enables immediate intervention to prevent team burnout.  \n- Creates email paper trail for resource allocation decisions."
      },
      "typeVersion": 1
    },
    {
      "id": "08cb35a9-0612-494b-bf9d-c3436b845d10",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        -688
      ],
      "parameters": {
        "width": 288,
        "height": 560,
        "content": "## \ud83d\udce2 Generate Over-Allocation Alert Report\n**Action:**  \n- Creates comprehensive alert message for over-allocated team members.  \n**Description:**  \n- Aggregates all over-allocation cases into single consolidated report.  \n- Formats message with:  \n  - Warning header with emoji indicators  \n  - List of affected team members with hours and percentages  \n  - Actionable recommendations for workload rebalancing  \n  - Timestamp for tracking and audit purposes  \n- Generates dynamic subject line with count of affected members.  \n- Handles edge case where no over-allocations exist (all-clear message).  \n- Prepares both summary and detailed data for notification delivery."
      },
      "typeVersion": 1
    },
    {
      "id": "79868a06-b2cf-4e68-b4ab-8b49fd6c5983",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        128
      ],
      "parameters": {
        "height": 496,
        "content": "## \u26a0\ufe0f Detect Over-Allocated Team Members\n**Action:**  \n- Filters team members whose utilization exceeds 100% capacity.  \n**Description:**  \n- Evaluates status field to identify \"Overallocated\" team members.  \n- **True Path:** Routes overloaded members to alert generation.  \n- **False Path:** Skips notification if all team members are within capacity.  \n- Critical decision point for proactive workload management.  \n- Prevents burnout by flagging capacity issues early."
      },
      "typeVersion": 1
    },
    {
      "id": "1fb0b12d-4aee-4ad0-80d5-90470013b097",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -816
      ],
      "parameters": {
        "height": 592,
        "content": "## \ud83d\udcc8 Log Capacity Data to Tracking Sheet\n**Action:**  \n- Records team member utilization metrics to Google Sheets for historical tracking.  \n**Description:**  \n- Appends each team member's capacity data to \"Team Capacity Tracking\" sheet.  \n- Columns logged: Assignee, Total Hours, Utilization %, Status, Timestamp.  \n- Creates time-series data for trend analysis and capacity planning.  \n- Enables visualization of workload patterns over sprints.  \n- Provides audit trail for retrospectives and resource allocation decisions.  \n- Supports data-driven sprint planning and burnout prevention."
      },
      "typeVersion": 1
    },
    {
      "id": "60bcfb26-5e75-4f78-96c6-4ce964030bfe",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -704
      ],
      "parameters": {
        "height": 656,
        "content": "## \ud83d\udcca Calculate Team Member Utilization\n**Action:**  \n- Aggregates time logged per team member and calculates capacity utilization.  \n**Description:**  \n- Processes all Jira issues to extract time spent data for each assignee.  \n- Converts time tracking from seconds to hours for readability.  \n- Calculates utilization percentage against 8-hour daily capacity baseline.  \n- Identifies over-allocated team members (>100% capacity).  \n- Outputs structured data for each team member:  \n  - Assignee name  \n  - Total hours logged  \n  - Utilization percentage  \n  - Status flag (OK / Overallocated)  \n- Handles unassigned tasks gracefully with \"Unassigned\" label"
      },
      "typeVersion": 1
    },
    {
      "id": "ea3a99a2-8388-400a-8835-e879e3952a84",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        448
      ],
      "parameters": {
        "height": 576,
        "content": "## \ud83d\udcca Log Query Failures to Error Sheet\n**Action:**  \n- Records Jira query failures or data retrieval errors to tracking spreadsheet.  \n**Description:**  \n- Appends error details to \"error log sheet\" when Jira returns no data.  \n- Captures API failures, authentication issues, or JQL syntax errors.  \n- Ensures no silent failures\u2014every execution is documented.  \n- Creates queryable database for troubleshooting integration issues.  \n- Helps identify patterns in API reliability or configuration problems.  \n- Critical for maintaining workflow reliability and SLA monitoring."
      },
      "typeVersion": 1
    },
    {
      "id": "ece752c2-40d2-4e9b-b21c-62c6d3ddfb51",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        -528
      ],
      "parameters": {
        "height": 480,
        "content": "## \u2705 Validate Issues Retrieved Successfully\n**Action:**  \n- Checks if Jira returned any active issues for processing.  \n**Description:**  \n- Verifies that the query successfully retrieved issue data.  \n- **True Path:** Proceeds to capacity calculation when issues exist.  \n- **False Path:** Routes to error logging if no data returned or query failed.  \n- Prevents downstream processing errors from empty datasets.  \n- Acts as a quality gate before expensive calculations."
      },
      "typeVersion": 1
    },
    {
      "id": "a44b5a35-d244-466c-8ef9-6ce5c155e655",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -352,
        192
      ],
      "parameters": {
        "height": 464,
        "content": "## \ud83d\udccb Fetch Active Jira Issues\n**Action:**  \n- Retrieves all in-progress Jira issues from the project.  \n**Description:**  \n- Queries Jira using JQL: \"statusCategory != Done AND status = 'In Progress'\".  \n- Pulls complete issue data including assignee, time tracking, and status.  \n- Captures current workload snapshot for capacity calculations.  \n- Returns all active tasks across all team members for comprehensive analysis.  \n- Essential data source for utilization metrics and over-allocation detection."
      },
      "typeVersion": 1
    },
    {
      "id": "da294676-ddf2-4411-82d1-cdfa2b64cb5b",
      "name": "Jira Get Issues Node",
      "type": "n8n-nodes-base.jira",
      "position": [
        -304,
        -16
      ],
      "parameters": {
        "options": {
          "jql": "=statusCategory != Done AND status = \"In Progress\"\n"
        },
        "operation": "getAll"
      },
      "credentials": {
        "jiraSoftwareCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5fdb6740-8798-43a0-bd92-bfe826ef6595",
      "name": "Data Validation",
      "type": "n8n-nodes-base.if",
      "position": [
        -80,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ef3f0536-f084-4c2b-9bf6-7cd172f90035",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $input.all().length > 0 }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2b109593-2b1e-4fb3-a4c4-b6bf9c228842",
      "name": "Capacity Calculator",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        -32
      ],
      "parameters": {
        "jsCode": "// In n8n, when \"Run Once for All Items\" is selected, use $input.all()\nconst items = $input.all();\n\nif (items.length === 0) {\n  return { json: { message: \"No issues found.\" } };\n}\n\n// Define a dictionary to store capacity data for each user\nconst userCapacity = {};\n\n// Loop through all items (each item is a Jira issue)\nitems.forEach(item => {\n  const issue = item.json;\n  \n  // Get assignee name\n  const assignee = issue.fields?.assignee?.displayName || 'Unassigned';\n  \n  // Get time spent in seconds\n  const timeSpent = issue.fields?.timespent || 0;\n  \n  // Convert timeSpent from seconds to hours\n  const hoursSpent = timeSpent / 3600;\n  \n  // Add the time spent to the user's total\n  if (!userCapacity[assignee]) {\n    userCapacity[assignee] = 0;\n  }\n  userCapacity[assignee] += hoursSpent;\n});\n\n// Now calculate utilization and flag over-allocated users\nconst utilizationData = [];\nconst maxCapacity = 8; // max available hours per day\n\nfor (let assignee in userCapacity) {\n  const totalHours = userCapacity[assignee];\n  const utilizationPercentage = (totalHours / maxCapacity) * 100;\n  \n  utilizationData.push({\n    assignee: assignee,\n    totalHours: parseFloat(totalHours.toFixed(2)),\n    utilizationPercentage: parseFloat(utilizationPercentage.toFixed(2)),\n    status: utilizationPercentage > 100 ? 'Overallocated' : 'OK'\n  });\n}\n\n// Return each user as a separate item for easier processing downstream\nreturn utilizationData.map(data => ({ json: data }));"
      },
      "typeVersion": 2
    },
    {
      "id": "30b80dd9-8b7e-4c08-adc7-4437a1119a18",
      "name": "Log Capacity Data to Tracking Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        464,
        -208
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "={{ $json.status }}",
            "Assignee": "={{ $json.assignee }}",
            "Total Hours": "={{ $json.totalHours }}",
            "Utilization %": "={{ $json.utilizationPercentage }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Assignee",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Assignee",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Hours",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Total Hours",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Utilization %",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Utilization %",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1334183067,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit#gid=1334183067",
          "cachedResultName": "Team Capacity Tracking"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit?usp=drivesdk",
          "cachedResultName": "Interviewer Brief Pack "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4303fbf4-2489-47d3-82d1-ce0633da1364",
      "name": "Over-Allocation Check",
      "type": "n8n-nodes-base.if",
      "position": [
        688,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "321c8920-d8a6-4f87-9faf-f082a037a8c6",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "={{ $json.status }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a915efc1-9f5f-4a28-b7c5-273737ce07fb",
      "name": "Alert Report Generator",
      "type": "n8n-nodes-base.code",
      "position": [
        1008,
        -112
      ],
      "parameters": {
        "jsCode": "// Aggregate all over-allocated users into one report\nconst items = $input.all();\n\nif (items.length === 0) {\n  return [{ json: { \n    subject: \"\u2705 Team Capacity Report - All Clear\",\n    message: \"No team members are currently over-allocated.\",\n    hasIssues: false\n  }}];\n}\n\n// Create report lines for each over-allocated person\nlet reportLines = items.map(item => \n  `\u2022 ${item.json.assignee}: ${item.json.totalHours}h logged (${item.json.utilizationPercentage}% capacity)`\n);\n\nconst message = `\u26a0\ufe0f TEAM OVER-ALLOCATION ALERT\\n\\n` +\n  `The following team members are over-allocated:\\n\\n` +\n  `${reportLines.join('\\n')}\\n\\n` +\n  `Recommended Action: Please review workload distribution and consider:\\n` +\n  `- Moving lower priority tasks to next sprint\\n` +\n  `- Redistributing work to available team members\\n` +\n  `- Extending deadlines if necessary\\n\\n` +\n  `Report generated: ${new Date().toLocaleString()}`;\n\nreturn [{ json: {\n  subject: `\u26a0\ufe0f Team Over-Allocation Alert - ${items.length} Member(s)`,\n  message: message,\n  overallocatedCount: items.length,\n  hasIssues: true,\n  details: items.map(i => i.json)\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "4624c013-7758-4fdf-b879-a90d86a706e0",
      "name": "Send Over-Allocation Alert to Manager",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1264,
        -112
      ],
      "parameters": {
        "toList": [
          "=newscctv22@gmail.com"
        ],
        "message": "={{ $json.message }}",
        "subject": "={{ $json.subject }}",
        "resource": "message",
        "additionalFields": {
          "ccList": []
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cd15b637-c0c3-4024-ae2c-55bd2262ea9e",
      "name": "Log Query Failures to Error Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        96,
        272
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "error_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "error",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "error_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1338537721,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit#gid=1338537721",
          "cachedResultName": "error log sheet"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit?usp=drivesdk",
          "cachedResultName": "Interviewer Brief Pack "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "aa6260ed-46d9-4e9a-bd42-e1f6fcc713e2",
  "connections": {
    "Data Validation": {
      "main": [
        [
          {
            "node": "Capacity Calculator",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Query Failures to Error Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capacity Calculator": {
      "main": [
        [
          {
            "node": "Over-Allocation Check",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Capacity Data to Tracking Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Jira Get Issues Node": {
      "main": [
        [
          {
            "node": "Data Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Over-Allocation Check": {
      "main": [
        [
          {
            "node": "Alert Report Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Report Generator": {
      "main": [
        [
          {
            "node": "Send Over-Allocation Alert to Manager",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Capacity Data to Tracking Sheet": {
      "main": [
        [
          {
            "node": "Over-Allocation Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Jira Get Issues Node",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}