Automate Epic Custom Field Sum

Overview

Sum up the total of custom field values of all related epic issues, including sub-tasks.

Example

There is a project that has a custom field named as "Cost". This custom field is available for all issue types. When an issue or sub-task is created or updated, you can define value of "Cost" field. If the issue or sub-task is related with an epic, "Cost" field of the epic is automatically calculated and the result is the sum of all related issues and sub-tasks "Cost" fields.

Good to Know

  • This script can bet set as a listener for "Issue created" and "Issue updated". So once an issue is created or updated sum field value will be recalculated.
  • Issues related with an epic are the ones that are child of the epic or child of those issues. To do this, the JQL "linkedissue" is used, but is needed to get rid of the epic issue itself.

Requirements

Jira Jira

//Epic Link Custom Field ID
final epicLinkCfId = get("/rest/api/2/field")
    .asObject(List)
    .body
    .find {
        (it as Map).name == 'Epic Link'
    }.id

//Number Custom Field ID
final numberCfId = get("/rest/api/2/field")
    .asObject(List)
    .body
    .find {
        (it as Map).name == ''
    }.id

def issueFields = get("/rest/api/2/issue/$issue.key")
    .asObject(Map)
    .body
    .fields

//If issue has no parent or is not related with an epic, the script will not be executed
def issueParent = issueFields.find { it.key == 'parent' }?.value
def epicKey = issueFields.find { it.key == epicLinkCfId }?.value
if (!epicKey && !issueParent) {
    return
}

//If the issue is sub-task, Epic Key is obtained from the parent issue
if (issueParent) {
    def parentIssueField = get("/rest/api/2/issue/$issueParent.key")
        .asObject(Map)
        .body
        .fields

    epicKey = parentIssueField.find { it.key == epicLinkCfId }.value
}

//Obtain all related epic issues, including sub-tasks
def allChildIssues = get("/rest/api/2/search")
    .queryString('jql', "linkedissue = $epicKey")
    .header('Content-Type', 'application/json')
    .asObject(Map)
    .body
    .issues

def sum = 0
def issues = allChildIssues.findAll { it.key != epicKey }

issues.each {
    def numberCfValue = get("/rest/api/2/issue/$it.key")
        .header('Content-Type', 'application/json')
        .asObject(Map)
        .body
        .fields[numberCfId] ?: 0
    sum += numberCfValue
}

put("/rest/api/2/issue/$epicKey")
    .header("Content-Type", "application/json")
    .body([
        fields: [
            (numberCfId): sum
        ]
    ]).asString()
Discovered an issue? Report it here

Suggested for you