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

Deleted user August 2, 2017

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

1 vote
Answer accepted
Henning Tietgens
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 3, 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

Deleted user August 3, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 3, 2017

Yes, you have to import the ApplicationUser class.

import com.atlassian.jira.user.ApplicationUser

Henning 

Deleted user August 3, 2017

Unbenannt.PNG

Henning Tietgens
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 3, 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

Deleted user August 3, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 3, 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

Deleted user August 4, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 2017

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

Is the customfield name correct? Maybe translated?

Deleted user August 4, 2017

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

Henning Tietgens
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 2017

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.

Deleted user August 4, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 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).

Deleted user August 4, 2017

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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 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
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 2017

I filed a bug at Adaptavist. 

Is the assignee working now?

Henning Tietgens
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 4, 2017

I think it has to be

getFieldById(ASSIGNEE).setFormValue(parentIssue.assignee?.name)
Deleted user August 9, 2017

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

Bill Joe January 13, 2020

I would like to add to your script one if before options

 

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

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events