Show the work remaining in all issues of the Composition link types

Sometimes you want to use the "Composition" or "part of" link types to break down large tasks rather than subtasks. However you can’t see the rolled up remaining estimate. This custom field shows the amount of time remaining on all issues that this issue comprises. The outbound link is Comprises and the inbound link would be is comprised of.

package com.onresolve.jira.groovy.test.scriptfields.scripts

import com.atlassian.jira.component.ComponentAccessor

def issueLinkManager = ComponentAccessor.getIssueLinkManager()

def totalRemaining = 0
issueLinkManager.getOutwardLinks(issue.id).each {issueLink ->
    if (issueLink.issueLinkType.name == "Composition") { (1)
        def linkedIssue = issueLink.destinationObject
        totalRemaining += linkedIssue.getEstimate() ?: 0
    }
}

// add the remaining work for this issue if there are children, otherwise it's just the same as the remaining estimate,
// so we won't display it,
if (totalRemaining) {
    totalRemaining += issue.getEstimate() ?: 0
}

return totalRemaining as Long ?: 0L
1 change the link type name to suit

The important thing is that this returns a Long, so that we can search on it, however that’s not very useful to display to users.

Select the Duration (time-tracking) template to make the display of the custom field match that of the Estimate field.

workRemaining

Template

Duration (time-tracking)

Searcher

Duration Searcher

You should also be able to search on this scripted field using the same syntax you can for other time tracking fields:

workRemainingSearch

Indexing

You may notice a further problem here. When work is logged on one of the linked issues, the issues that link to it don’t get reindexed, so a search won’t return the correct results.

To handle this, we use a custom Script Listener to follow "is comprised of" links and reindex those issues:

package com.onresolve.jira.groovy.test.scriptfields.scripts

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.index.IssueIndexManager

def issue = event.issue // event is an IssueEvent
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def issueIndexManager = ComponentAccessor.getComponent(IssueIndexManager)

issueLinkManager.getInwardLinks(issue.id).each {issueLink ->
    if (issueLink.issueLinkType.name == "Composition") { (1)
        def linkedIssue = issueLink.getSourceObject()
        issueIndexManager.reIndexIssueObjects([linkedIssue])
    }
}
1 change the link type name to suit

This is configured as so:

reindexLinkedIssuesListener

For how-to questions please ask on Atlassian Answers where there is a very active community. Adaptavist staff are also likely to respond there.

Ask a question about ScriptRunner for JIRA, for for Bitbucket Server, or for Confluence.