Copy custom field (user object) to sub-task using script runner behaviour

I have a custom user picker field on both the parent and sub-task. When a sub-task is created, I would like to copy the user specified in the parent task to the same field in the sub-task. The field is called "Projektleiter".

I tried using the example in the ScriptRunner documentation but it doesn't work. Here's what I have.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.web.util.OutlookDate
import com.atlassian.jira.web.util.OutlookDateManager
import com.onresolve.jira.groovy.user.FieldBehaviours
import com.onresolve.jira.groovy.user.FormField
import groovy.transform.BaseScript

import java.sql.Timestamp

import static com.atlassian.jira.issue.IssueFieldConstants.*

@BaseScript FieldBehaviours fieldBehaviours

FormField field = getFieldById(getFieldChanged())
FormField parent = getFieldById("parentIssueId")
Long parentIssueId = parent.getFormValue() as Long

if (!parentIssueId || field.getFormValue()) {
    // this is not a subtask, or the field already has data
    return
}

def issueManager = ComponentAccessor.getIssueManager()
def parentIssue = issueManager.getIssueObject(parentIssueId)
def customFieldManager = ComponentAccessor.getCustomFieldManager()

OutlookDate outlookDate = ComponentAccessor.getComponent(OutlookDateManager).getOutlookDate(Locale.getDefault())

// REMOVE OR MODIFY THE SETTING OF THESE FIELDS AS NECESSARY
getFieldById(COMPONENTS).setFormValue(parentIssue.componentObjects*.id)

// IF YOU DON'T WANT CUSTOM FIELDS COPIED REMOVE EVERYTHING BELOW HERE
// IF YOU ONLY WANT SOME FIELDS INHERITED ADD THEM TO THE LIST BELOW, OR LEAVE EMPTY FOR ALL
// eg ['Name of first custom field', 'Name of second custom field']
List copyCustomFields = ['Projektleiter']

List<CustomField> parentFields = customFieldManager.getCustomFieldObjects(parentIssue)

for (def cf in parentFields) {
if (copyCustomFields && !copyCustomFields.contains(cf.name)) {
        return
    }

def parentValue = cf.getValue(parentIssue) as List<Option>

if (!parentValue) {
        return
    }

getFieldById(cf.id).setFormValue(parentValue)
    log.debug("parentValue: ${parentValue?.class} for type ${cf.name}")

if (parentValue instanceof Timestamp) {
        getFieldById(cf.id).setFormValue(outlookDate.formatDMY(parentValue))
    } else if (parentValue instanceof Option) {
        getFieldById(cf.id).setFormValue(parentValue.optionId)
    } else if (parentValue instanceof List && parentValue[0] instanceof Option) {
        getFieldById(cf.id).setFormValue(parentValue*.optionId)
    } else {
        getFieldById(cf.id).setFormValue(parentValue)
    }
}



Maybe it's different for a user field. Can somebody help?

Thanks and BR.

1 answer

1 accepted

This widget could not be displayed.
Henning Tietgens Community Champion Aug 03, 2017

The value of a user picker field is not List<Option>. You have to use ApplicationUser as class. Or List<ApplicationUser> if it's a multi user picker.

def parentValue = cf.getValue(parentIssue) as ApplicationUser

The part including and below if (parentValue instanceof Timestamp) { can be removed.
Best,
Henning

Hi Henning

Thanks for your help so far.

After adding "as ApplicationUser", I get the error "Unable to resolve class ApplicationUser". The field is only a single user picker.

Do I have to import something maybe for it to work?

Thanks and BR

Marius

Henning Tietgens Community Champion Aug 03, 2017

Yes, you have to import the ApplicationUser class.

import com.atlassian.jira.user.ApplicationUser

Henning 

Henning Tietgens Community Champion Aug 03, 2017

Ok, I took a look at the original script and I think there is only one thing missing for handling user fields. I added it to the script:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.util.OutlookDate
import com.atlassian.jira.web.util.OutlookDateManager
import com.onresolve.jira.groovy.user.FieldBehaviours
import com.onresolve.jira.groovy.user.FormField
import groovy.transform.BaseScript

import java.sql.Timestamp

import static com.atlassian.jira.issue.IssueFieldConstants.*

@BaseScript FieldBehaviours fieldBehaviours

FormField field = getFieldById(getFieldChanged())
FormField parent = getFieldById("parentIssueId")
Long parentIssueId = parent.getFormValue() as Long

if (!parentIssueId || field.getFormValue()) {
// this is not a subtask, or the field already has data
return
}

def issueManager = ComponentAccessor.getIssueManager()
def parentIssue = issueManager.getIssueObject(parentIssueId)
def customFieldManager = ComponentAccessor.getCustomFieldManager()

// REMOVE OR MODIFY THE SETTING OF THESE FIELDS AS NECESSARY
getFieldById(SUMMARY).setFormValue(parentIssue.summary)
getFieldById(PRIORITY).setFormValue(parentIssue.getPriorityObject().id)

OutlookDate outlookDate = ComponentAccessor.getComponent(OutlookDateManager).getOutlookDate(Locale.getDefault())
getFieldById(DUE_DATE).setFormValue(outlookDate.formatDMY(parentIssue.getDueDate()))

getFieldById(COMPONENTS).setFormValue(parentIssue.componentObjects*.id)
getFieldById(AFFECTED_VERSIONS).setFormValue(parentIssue.affectedVersions*.id)
getFieldById(FIX_FOR_VERSIONS).setFormValue(parentIssue.fixVersions*.id)
getFieldById(ASSIGNEE).setFormValue(parentIssue.assigneeId)
getFieldById(ENVIRONMENT).setFormValue(parentIssue.environment)
getFieldById(DESCRIPTION).setFormValue(parentIssue.description)
getFieldById(SECURITY).setFormValue(parentIssue.securityLevelId)
getFieldById(LABELS).setFormValue(parentIssue.labels)

// IF YOU DON'T WANT CUSTOM FIELDS COPIED REMOVE EVERYTHING BELOW HERE
// IF YOU ONLY WANT SOME FIELDS INHERITED ADD THEM TO THE LIST BELOW, OR LEAVE EMPTY FOR ALL
// eg ['Name of first custom field', 'Name of second custom field']
List copyCustomFields = []

List<CustomField> parentFields = customFieldManager.getCustomFieldObjects(parentIssue)

for (def cf in parentFields) {
if (copyCustomFields && !copyCustomFields.contains(cf.name)) {
return
}

def parentValue = cf.getValue(parentIssue)

if (!parentValue) {
return
}

log.debug("parentValue: ${parentValue?.class} for field ${cf.name}")

if (parentValue instanceof Timestamp) {
getFieldById(cf.id).setFormValue(outlookDate.formatDMY(parentValue as Timestamp))
} else if (parentValue instanceof Option) {
getFieldById(cf.id).setFormValue((parentValue as Option).optionId)
} else if (parentValue instanceof List && parentValue[0] instanceof Option) {
getFieldById(cf.id).setFormValue((parentValue as List<Option>)*.optionId)
} else if (parentValue instanceof ApplicationUser) {
getFieldById(cf.id).setFormValue((parentValue as ApplicationUser).name)
} else {
getFieldById(cf.id).setFormValue(parentValue)
}
}

I think setting a user field via setFormValue() has to use the username (ApplicationUser.name) and not the ApplicationUser object.

Please make your changes and try it again.

Best regards,
Henning

There was a small error in the last if condition.

grafik.png

I removed that particular else if part because I think it's not necessary for my case and then saved the script. However, when I opened the parent task and clicked on Create Sub-Task the field Projektleiter is still empty.

grafik.png

grafik.png

I also noticed that the assignee has not been set either. Summary, Components, Priority and Due Date have been successfully set. Not that I necessarily want the assignee to be set. I just think it's suspicious that both user fields seem to have issues.

BR

Marius

Henning Tietgens Community Champion Aug 03, 2017

Do you see DEBUG messages in the atlassian-jira.log? If not could you try to add 

import org.apache.log4j.Level
log.setLevel(Level.DEBUG)

to the code at the beginning and try again.

Henning

I've added both lines and also this one.

log.debug("--------------------This is to test Behaviours Logging----------------")

Unfortunately, this test message is the only debug message I'm able to find in the log file after trying to create a sub-task.

Henning Tietgens Community Champion Aug 04, 2017

Mmh, so we need more  debug messages. Try before both return statements.

Is the customfield name correct? Maybe translated?

Added:

log.debug("1. Return")

and

log.debug("2. Return")

After testing, I can see the message "1. Return" in the log file. However, I tried commenting out these to if conditions before and it wouldn't copy the user either way. But still odd that it enters the first if condition.

The name is correct. It's in German.

grafik.png

I know, I'm a native German speaker. My question was because if the user has a different language selected and the customfield is translated, it may not match.

Nevertheless let's try to see what the list of customfields of the parent issue contains. Please add 

log.debug "ParentFields: ${parentFields*.name}"

after List<CustomField> parentFields = customFieldManage... and try again.

Ah, I see. We're currently not working with translations. Everything is usually in German.

Here's the log message.

ParentFields: [Beobachter, Checkliste, Hide Comment Field, Kasse, Lieferant(en) & Partner, Projektleiter, issueFunction]*.name
Henning Tietgens Community Champion Aug 04, 2017

For every field besides Projektleiter there should be a "1. Return" debug message.

Please change log.debug("1. Return") to 

log.debug("1. Return: $cf.name")

 and add

log.debug "ParentValue: $parentValue"

below def parentValue = cf.getValue(parentIssue).

I think we got it!

After applying your changes:

--------------------This is to test Behaviours Logging----------------
ParentFields: [Beobachter, Checkliste, Hide Comment Field, Kasse, Lieferant(en) & Partner, Projektleiter, issueFunction]*.name
1. Return: Beobachter

I then tried and replaced the return statement with 'continue':

--------------------This is to test Behaviours Logging----------------
ParentFields: [Beobachter, Checkliste, Hide Comment Field, Kasse, Lieferant(en) & Partner, Projektleiter, issueFunction]*.name
1. Return: Beobachter
1. Return: Checkliste
1. Return: Hide Comment Field
1. Return: Kasse
1. Return: Lieferant(en) & Partner
ParentValue: VollrathM(vollrathm)
parentValue: class com.atlassian.jira.user.DelegatingApplicationUser for field Projektleiter
1. Return: issueFunction

grafik.png

The field is now filled correctly! :)

What do you think? Is this the correct way?

Henning Tietgens Community Champion Aug 04, 2017

Yes! That's easy (as always :-)).

Someone changed the source in the documentation.. in version 5.0.7 it's using "parentFields.each { CustomField cf ->" instead of the for loop (since v5.0.8 of the documentation). Within each{} a "return" leaves the closure only for one element. In a for loop it has nothing special to say, so it leaves the script...

I normaly don't use for loops so I didn't catched it :-) Well done!

Henning Tietgens Community Champion Aug 04, 2017

I filed a bug at Adaptavist. 

Is the assignee working now?

I think it has to be

getFieldById(ASSIGNEE).setFormValue(parentIssue.assignee?.name)

Hi Henning

Unfortunately, the assignee still does not work. However, since my original post is solved, I'll go ahead and mark your answer as accepted :)

Thank you again for your support. I'm glad we got it working.

BR

Marius

Suggest an answer

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

What modern development practices are at the heart of how your team delivers software?

Hey Community mates! Claire here from the Software Product Marketing team. We all know software development changes rapidly, and it's often tough to keep up. But from our research, we've found the h...

178 views 1 3
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