Is there a way to ensure/enforce fix version so that sub tasks match the parent issue?

Right now it is possible to have a mismatch of fix version between parent and sub task.  I want to ensure that this is not possible.  No value is fine, as long as all tickets are empty.

Script Runner or other paid plugins are not an option.

1 answer

1 accepted

This widget could not be displayed.

I have a Script Runner script listener for this exact case, though I'm also syncing a custom field called "Verified Version/s".  You probably don't need that, but I'll include it as an example.  It's implemented as a groovy class file, here is a sanitized version:

import com.atlassian.jira.event.issue.AbstractIssueEventListener;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.project.version.Version;
import com.atlassian.jira.config.SubTaskManager;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.event.type.EventDispatchOption;
import com.atlassian.jira.workflow.WorkflowManager;
import com.atlassian.jira.workflow.JiraWorkflow;
import java.util.ArrayList;
import java.util.Collection;
class SyncParentFixVerifiedVersionsToSubTask extends AbstractIssueEventListener 
    Category log = Category.getInstance(SyncParentFixVerifiedVersionsToSubTask.class);
    SubTaskManager subTaskManager = ComponentManager.getInstance().getSubTaskManager();
    WorkflowManager workflowManager = ComponentManager.getInstance().getWorkflowManager();
    IssueManager issueManager = ComponentManager.getInstance().getIssueManager();
    CustomFieldManager customFieldManager = ComponentManager.getInstance().getCustomFieldManager();
    Boolean changed = false;
    Long workflowId;
    void workflowEvent(IssueEvent event) {
        try {
            if (subTaskManager.isSubTasksEnabled()) {
                Issue eventIssue = event.getIssue();
                JiraWorkflow workflow = workflowManager.getWorkflow(eventIssue);
                if (workflow.getName() == "Put Workflow Name Here") {
                    CustomField vvcf = customFieldManager.getCustomFieldObjectByName("Verified Version/s");
                    if ( !eventIssue.getIssueTypeObject().isSubTask() ) {
                        // Change is on a potential parent, sync to sub-tasks if there are any
                        Collection<Issue> subTasks = eventIssue.getSubTaskObjects();
                        List changeItems = event.getChangeLog().getRelated("ChildChangeItem");
                        // Sync FixVersion/s
                        if( changeItems.any {it.get('field')=='Fix Version'} ) {
                            changed = true;
                            // Collection<Version> fixVersions = new ArrayList<Version>();
                            // fixVersions = eventIssue.getFixVersions();
                            Collection<Version> fixVersions = eventIssue.getFixVersions();
                            if (!subTasks.isEmpty()) {
                                subTasks.each {
                        // Sync Verified Version/s
                        if( changeItems.any {it.get('field')=='Verified Version/s'} ) {
                            changed = true;
                            Collection<Version> verifiedVersions = new ArrayList<Version>();
                            verifiedVersions = eventIssue.getCustomFieldValue(vvcf);
                            if (!subTasks.isEmpty()) {
                                subTasks.each {
                        if (changed) {
                            subTasks.each {
                                issueManager.updateIssue(event.getUser(), it, EventDispatchOption.ISSUE_UPDATED, false);
                    } else {
                        // Change is on a sub-task, sync from the parent if they are out of sync
                        Issue parentIssue = eventIssue.getParentObject();
                        // Sync "Fix Version/s"
                        Collection<Version> parentFixVersions = parentIssue.getFixVersions();
                        if (parentFixVersions == null) parentFixVersions = new ArrayList<Version>();
                        Collection<Version> fixVersions = eventIssue.getFixVersions();
                        if (fixVersions == null) fixVersions = new ArrayList<Version>();
                        if(!(parentFixVersions.containsAll(fixVersions) && fixVersions.containsAll(parentFixVersions))) {
                            changed = true;
                        // Sync "Verified Version/s"
                        Collection<Version> parentVerifiedVersions = parentIssue.getCustomFieldValue(vvcf);
                        if (parentVerifiedVersions == null) parentVerifiedVersions = new ArrayList<Version>();
                        Collection<Version> verifiedVersions = eventIssue.getCustomFieldValue(vvcf);
                        if (verifiedVersions == null) verifiedVersions = new ArrayList<Version>();
                        if(!(parentVerifiedVersions.containsAll(verifiedVersions) && verifiedVersions.containsAll(parentVerifiedVersions))) {
                            changed = true;
                        if (changed) {
                            issueManager.updateIssue(event.getUser(), eventIssue, EventDispatchOption.ISSUE_UPDATED, false);
        catch (ex) {
            log.debug "Event: ${event.getEventTypeId()} fired for ${event.issue} and caught by SyncParentFixVerifiedVersionsToSubTask"
            log.debug (ex.getMessage())

I added the workflow name check so that the listener can be set for all projects, that way you don't risk adding a project and forgetting to add it.  That worked for me, but may not for you.

Having said all of that, I've heard people mention that there is some built-in way to sync that field, but I don't have details.  It may be that they can only be synced on sub-task creation.  Speaking of, the checking for an update to the sub-task is to handle the case where you add a sub-task, or move an issue to a sub-task, etc.

WOW!  Thanks!  I should have mentioned Script Runner isn't really an option for us since it's no longer free, and installing it will force us to remain at version 6.  We're not using Script Runner now - I was tempted, but shortly after installation it went from free to fee.  

I may be wrong about getting Script Runner - I'll be giving this a try soon!  Thanks again!!

@Jeremy Gaudet I am looking for a solution to the same question too. So, in script listener, we are supposed to place a groovy file in the listeners folder right ? So, what would be the content of that file. Is it the same script as above ? Thanks 

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted yesterday in Jira

Atlassian Research Workshop opportunity on Sep. 28th in Austin, TX

We're looking for participants for a workshop at Atlassian! We need Jira admins who have interesting custom workflows, issue views, or boards. Think you have a story to sha...

40 views 2 2
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you