Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Get issue id of last created issue, and check issue type

- November 30, 2020

Referencing the round robin assignment script from adaptavist library:https://library.adaptavist.com/entity/round-robin-assign-issue-to-users-in-a-certain-project-role, I want to get the last issue created in a project, but i also want to focus in on a certain issue type. 

Here's what I changed in the script:

def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {issue.getAsString("issuetype") == "Certification" }

So with this, I thought I'd be able to find the last Certification issue created in that project, but this doesn't seem to be the case...because the assignee keeps defaulting to the first user listed in the user key array.

Is there anything wrong with the change I made to the script?

1 answer

1 accepted

Suggest an answer

Log in or Sign up to answer
1 vote
Answer accepted
Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 1, 2020

Hi @Ian Balas ,

please try to change it like this:

def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {
def foundIssue = issueManager.getIssueObject(it)
return foundIssue.assignee && foundIssue.issueType.name == "Certification"
}
- December 1, 2020

@Hana Kučerová Works like a charm! Thank you so much :)

 

For learning purposes, how did you know to re-declare the issue id and set the return types for issue assignee and issue type? Does that part of the code serve as the condition of the find function? (Like return the first issue that has an assignee and as of Certification type?)

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 1, 2020

@Ian Balas yes, find method finds the first value in a collection that matches some criterion.

1) find issue ids for project (getIssueIdsForProject returns Collection<Long>)

def ids = issueManager.getIssueIdsForProject(issue.projectObject.id)

2) ids are sorted, reversed

ids
.sort()
.reverse()

3) search through the ids and find the first issue with some specific data


.find { issueManager.getIssueObject(it).assignee }

We need to get the issue object based on provided id (variable it) to be able to find out if it has an assignee and specific issue type name

I used foundIssue variable, because I didn't want to search for the issue object twice

This part

def foundIssue = issueManager.getIssueObject(it)
return foundIssue.assignee && foundIssue.issueType.name == "Certification"

Is basically the same as

issueManager.getIssueObject(it).assignee && issueManager.getIssueObject(it).issueType.name == "Certification"

So I only added the second condition, which also needs to be true (if issue has assignee and it's issue type is "Ceritifcation", it matches the criterion and it's id is returned)

lastIssueIdWithAssignee could be null, if there is no such issue, or it is the issue id of found issue (because we are iterating through the ids)

- December 1, 2020

@Hana Kučerová I guess I must have been pointing to the issue I was creating in the post function rather that getting the issue object based on id. In other words, I wasn't searching for the issue based on the provided id, but was rather pointing to the issue I was creating in the post function (Feel free to correct me if I'm wrong).

But I do understand your solution, and now know a little more about the find method thanks to you.

Again, many thanks!

-Ian

- December 3, 2020

@Hana Kučerová One more thing...I hope this isn't too much to ask, but is there a way to add a condition to see if the assignee was set via automation (the script is run as user "automation")? A problem came up where an issue, which was originally assigned through this round robin script, was manually re-assigned...and then the assignee for the next Certification issue created afterwards was automatically set to the first assignee in the round robin user list.

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 3, 2020

@Ian Balas Hi, to be honest, I don't fully understand your problem. Do I get it right you want to exclude all the issues, where the last assign action was made by automation user?

- December 3, 2020

@Hana Kučerová Yeah that's what I'm looking for, but still focus just on Certification issues.

Here's some user feedback from my end regarding what they want:

"we want the tickets to be auto-assigned in that order (Ben M, Kristen B, Minyoung K, Mari L), regardless of re-assignment.

So, for example, a ticket is auto-assigned to Ben. However, let's say Ben is on vacation this week. Therefore, we manually reassign the ticket to Minyoung. If the round robin assignment order is Ben, Kristen, Minyoung, Mari, we still want the next ticket to be auto-assigned to Kristen given this is the auto-assignment order."

So with this feedback, I assume the way to make this possible were if I could make sure the previous Certification issue was auto-assigned by the user "automation" (since the script in the post function is ran as user "automation") and not manually reassigned.

Let me know if this makes sense!

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 3, 2020

@Ian Balas Thank for the clarification, now I get it... It could be done, but I don't like the idea of looping through each issue's history and search for the author of the last assignee change... what about to store author of the last assignee change to some custom field and use this custom field in the condition instead of searching in the history? Or use some other custom field to mark the issues, which should be excluded from the automation?

- December 3, 2020

@Hana Kučerová I like the idea of storing the author of the last assignee change in a custom field and using that in the condition. So in this case, the issue object to be found would be one that is of certification type, and the author of the last assignee change would have to come up as automation.

- December 8, 2020

Hi @Hana Kučerová ,

Thanks again for the tip regarding the round robin solution I was looking for.

As for the solution you offered regarding the scripted field, which would store the author of the last assignee change, I tried creating a field called Assignee Last Changed By:, and added this script so far (in reference to https://community.atlassian.com/t5/Jira-questions/How-to-get-previous-changed-custom-field-data/qaq-p/829463):

import com.atlassian.jira.component.ComponentAccessor

def changeHistoryManager = com.atlassian.jira.component.ComponentAccessor.getChangeHistoryManager()
def old = changeHistoryManager.getChangeItemsForField(issue, 'Assignee')
return old.fromString

This doesn't seem to return anything unfortunately :( just a result of "Anonymous" along with an empty log record each time, even when testing on issues where users manually changed the assignee field.

Would you happen to know how I could improve this scripted field? 

(FYI I created another post specific to this additional ask https://community.atlassian.com/t5/Jira-questions/Get-user-author-who-last-changed-Assignee-field/qaq-p/1552568#M457256)

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 10, 2020

Hi @Ian Balas

I would recommend you to use "User Picker (single user)" field instead of scripted one and store the event actor to it everytime the field Assignee is changed.

Customer listener defined for you project and event "Issue updated" will hopefully do the trick (xxxxx needs to be replaced by your custom field id)

The listener script basically checks, whether field Assignee has been changed during update. If so, it stores the user, who changed the issue, to the custom field.

Please try.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.event.issue.IssueEvent
import static com.atlassian.jira.issue.IssueFieldConstants.ASSIGNEE
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.user.ApplicationUser
import org.ofbiz.core.entity.GenericValue

String userCustomFieldName = "customfield_xxxxx"
MutableIssue issue = event.getIssue()

IssueManager issueManager = ComponentAccessor.getIssueManager()
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
CustomField userCustomField = customFieldManager.getCustomFieldObject(userCustomFieldName)

GenericValue change = event?.getChangeLog()?.getRelated("ChildChangeItem")?.find {it.field == ASSIGNEE}
if (!change) {
return
}
ApplicationUser newUser = event.getUser()
ApplicationUser oldUser = userCustomField.getValue(issue) as ApplicationUser
if (newUser != oldUser) {
issue.setCustomFieldValue(userCustomField, newUser)
issueManager.updateIssue(newUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
}

 

- December 11, 2020

You're a genius @Hana Kučerová ! I really appreciate you going above and beyond with this.

After applying this listener script and the user picker, I can report that this is definitely a great addition to have along with the original round robin script. It's an improvement because now if a Certification issue is manually re-assigned, the next issue will auto-assign to the user in the round robin queue that was originally auto-assigned to the previous issue, as opposed to starting over and assigning the issue to the first user in the round robin queue.

To further explain in accordance to our round robin order (Ben M, Kristen B, Minyoung K, Mari L), Here's an example of how this works now from our use case:

"Minyoung K was auto-assigned to a cert issue, issueA via round robin. Afterwards, another cert issue ,issueB, is created and auto-assigns Mari L as expected. issueB is then manually assigned to another user. If you create another cert issue, issueC, right after that, it'll be auto-assigned to Mari L again"

So this works almost 100% the way we want it. Ideally, issueC should have auto-assigned to Ben M, regardless of the manual re-assignment.

We'll make do with what we have if this is the best it can be...but is there a way for the script to auto-assign to the next user in queue no matter what? If not, it's okay :)

Again, I really appreciate all the help you've provided up to this point!

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 12, 2020

Hi @Ian Balas ,

thank you :-). I'm really happy to help. Let's try to make it 100% :-).

It seems to me, that another user custom field could help here - to store the information, who was originally assigned to the issue by round robin script. Then the round robin will use this user instead of assignee to get the next user in the row. Does this make sense to you? If so, I can improve the listener script to handle this new custom field. We will also need to change the round robin script itself. I'm not sure about the current status of the script, so maybe it will be the best, if you paste it here first. Thank you.

- December 15, 2020

@Hana Kučerová I like that idea.

Here's the round robin script. I have it set as a scripted operation on issue post-function upon issue creation:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.roles.ProjectRoleManager

// The role you want assignees to set from
final roleName = 'Workers'

// If it is true, the assigned issues will be reassigned
final reassignedIssues = true

def issueManager = ComponentAccessor.issueManager
def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)

// Get all of the users associated with the specified project role
def projectRole = projectRoleManager.getProjectRole(roleName)

// Sort the users of the project role using the user key
def users = projectRoleManager.getProjectRoleActors(projectRole, issue.projectObject)
.applicationUsers
.toSorted { it.key }

// There are no users in the specific project role
if (!users) {
log.info ("No users for project role $roleName")
return
}

if (!reassignedIssues && issue.assignee) {
log.info ('The issue is already assigned')
return
}

// Find the latest created issue id that has an assignee
def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {
def foundIssue = issueManager.getIssueObject(it)
return foundIssue.assignee && foundIssue.issueType.name == "Certification" && !foundIssue.get("customfield_20500")
}

if (!lastIssueIdWithAssignee) {
issue.setAssignee(users.first())
return
}

def lastAssignee = issueManager.getIssueObject(lastIssueIdWithAssignee).assignee
def lastAssigneeIndex = users.indexOf(lastAssignee)
def nextAssignee = users[(lastAssigneeIndex + 1) % users.size()]

issue.setAssignee(nextAssignee)

Notice the changes you made to the lastIssueIdWithAssignee variable. Here, I added the condition, " &&  !foundIssue.get("customfield_20500")". This custom field translates to a user picker called Assignee Change Author, which I created along with the listener as per your other suggestion. If a user manually re-assigns an issue, their name will populate to that field...otherwise, the field will remain empty.

So now this script looks for the latest Certification issue, with an assigneewhere the Assignee Change Author field is empty.

Thanks again for all the help!!

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 20, 2020

Hi @Ian Balas ,

in the end I just updated your round Robin script. Everytime this script assigns some issue, it should also store the original assignee to the user custom field, which you will need to create for such purpose.

What is great - it seems to me, that the script listener and the customfield_20500 is not needed anymore. Because I think what you need is to find last assigned issue (and it doesn't matter, whether the assignee was later manually changed or not) and get original assignee from here (who is now stored in the new user custom field).

Does it make sense to you?

Please try the updated round robin script (xxxxx needs to be replaced with the id of the new user custom field):

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.roles.ProjectRoleManager

// The role you want assignees to set from
final roleName = 'Workers'
final originalAssigneeCustomFieldName = "customfield_xxxxx"

// If it is true, the assigned issues will be reassigned
final reassignedIssues = true

def issueManager = ComponentAccessor.issueManager
def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def originalAssigneeCustomField = customFieldManager.getCustomFieldObject(originalAssigneeCustomFieldName)

// Get all of the users associated with the specified project role
def projectRole = projectRoleManager.getProjectRole(roleName)

// Sort the users of the project role using the user key
def users = projectRoleManager.getProjectRoleActors(projectRole, issue.projectObject)
.applicationUsers
.toSorted { it.key }

// There are no users in the specific project role
if (!users) {
log.info ("No users for project role $roleName")
return
}

if (!reassignedIssues && issue.assignee) {
log.info ('The issue is already assigned')
return
}

// Find the latest created issue id that has an assignee
def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {
def foundIssue = issueManager.getIssueObject(it)
return foundIssue.assignee && foundIssue.issueType.name == "Certification"
}

if (!lastIssueIdWithAssignee) {
issue.setAssignee(users.first())
return
}

def issue = issueManager.getIssueObject(lastIssueIdWithAssignee)

def lastAssignee = issue.getCustomFieldValue(originalAssigneeCustomField) ?: issue.assignee
def lastAssigneeIndex = users.indexOf(lastAssignee)
def nextAssignee = users[(lastAssigneeIndex + 1) % users.size()]

issue.setAssignee(nextAssignee)
issue.setCustomFieldValue(originalAssigneeCustomField, nextAssignee)
- December 21, 2020

Hi @Hana Kučerová ,

This doesn't seem to work as intended unfortunately :(

So I went ahead and replaced the round robin script I had with your version instead. I created a field called Original Assignee, and form my understanding that field is supposed to pre-fill with the first assignee of that issue...presumptively after the round robin script auto-assigns. Instead, when I create the next certification issue no matter who the assignee is in the previous issue, the assignee field is left unassigned and nothing prefills in the Original Assignee field.

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 21, 2020

Hi @Ian Balas ,

sorry, please try to add your custom field Original Assignee to the issue create screen.

Please also try to check the log, if there's some error - probably there is one, because both the fields are not working.

- December 21, 2020

Hi @Hana Kučerová ,

Okay I added the field to the create screen but that didn't seem to help.

I ran a server log audit right when I created the issue and these following errors were logged:

 

2020-12-21 21:26:29,691+0000 http-nio-8080-exec-402 INFO ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.a.j.authenticator.okta.OktaJiraAuthenticator30] Authentication result=USER_ALREADY_LOGGED_IN
2020-12-21 21:26:29,867+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] *************************************************************************************
2020-12-21 21:26:29,867+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] Script field failed on issue: CERT-3325, field: Assignee Last Changed By
java.util.NoSuchElementException: Cannot access last() element from an empty List
 at Script7.run(Script7.groovy:4)
2020-12-21 21:26:29,936+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] *************************************************************************************
2020-12-21 21:26:29,936+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] Script field failed on issue: CERT-3325, field: Assignee Last Changed By
java.util.NoSuchElementException: Cannot access last() element from an empty List
 at Script7.run(Script7.groovy:4)
2020-12-21 21:26:30,009+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] *************************************************************************************
2020-12-21 21:26:30,009+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] Script field failed on issue: CERT-3325, field: Assignee Last Changed By
java.util.NoSuchElementException: Cannot access last() element from an empty List
 at Script7.run(Script7.groovy:4)
2020-12-21 21:26:30,431+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] *************************************************************************************
2020-12-21 21:26:30,431+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] Script field failed on issue: CERT-3325, field: Assignee Last Changed By
java.util.NoSuchElementException: Cannot access last() element from an empty List
 at Script7.run(Script7.groovy:4)
2020-12-21 21:26:30,644+0000 httpclient-callbacks:thread-266 WARN anonymous     [c.a.webhooks.plugin.PublishTaskFactoryImpl$PublishTaskImpl] Client error - 404 when posting to web hook at 'https://hooks.slack.com/services/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'
    
2020-12-21 21:26:30,692+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] *************************************************************************************
2020-12-21 21:26:30,692+0000 http-nio-8080-exec-402 ERROR ibalas 1286x180169x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.o.scriptrunner.customfield.GroovyCustomField] Script field failed on issue: CERT-3325, field: Assignee Last Changed By
java.util.NoSuchElementException: Cannot access last() element from an empty List
 at Script7.run(Script7.groovy:4)
- December 21, 2020

@Hana Kučerová 

Actually, please disregard these logs. These are auditing the Assignee Last Changed By scripted field I created earlier, which I forgot to remove.

Running the audit after removing this, however, doesn't seem to point out any errors in creating the issue:

2020-12-21 21:52:30,653+0000 http-nio-8080-exec-376 INFO ibalas 1312x181230x1 174kpd7 184.165.15.13,52.13.84.207,10.1.0.7 /secure/QuickCreateIssue.jspa [c.a.j.p.webhooks.matcher.JqlEventMatcher_SLOW] JQL query '{project = "WFS"} AND {issuetype in ("Support Request", "Defect")} AND {key in ("CERT-3331")}' produced lucene query and took '96' ms to run.

 This was all that came up in the audit log

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 22, 2020

Hi @Ian Balas ,

I apologize, it couldn't work. The main problem is with usage of setCustomFieldValue, I just assumed it behaves the same as setAssignee.

What needs to be set:

  1. Original Assignee field is on the create issue screen
  2. Post-functions are in the correct order

post.png

Here is the updated script (xxxxx needs to be replaced by your id) - I tested it and it works for me now.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.security.roles.ProjectRoleManager

// The role you want assignees to set from
final roleName = 'Workers'
final originalAssigneeCustomFieldName = "customfield_xxxxx"

// If it is true, the assigned issues will be reassigned
final reassignedIssues = true

def issueManager = ComponentAccessor.issueManager
def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def originalAssigneeCustomField = customFieldManager.getCustomFieldObject(originalAssigneeCustomFieldName)

// Get all of the users associated with the specified project role
def projectRole = projectRoleManager.getProjectRole(roleName)

// Sort the users of the project role using the user key
def users = projectRoleManager.getProjectRoleActors(projectRole, issue.projectObject)
.applicationUsers
.toSorted { it.key }

// There are no users in the specific project role
if (!users) {
log.info ("No users for project role $roleName")
return
}

if (!reassignedIssues && issue.assignee) {
log.info ('The issue is already assigned')
return
}

// Find the latest created issue id that has an assignee
def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {
def foundIssue = issueManager.getIssueObject(it)
return foundIssue.assignee && foundIssue.issueType.name == "Certification"
}

if (!lastIssueIdWithAssignee) {
issue.setAssignee(users.first())
def changeHolder = new DefaultIssueChangeHolder()
originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, users.first()), changeHolder)
return
}

def lastIssue = issueManager.getIssueObject(lastIssueIdWithAssignee)

def lastAssignee = lastIssue.getCustomFieldValue(originalAssigneeCustomField) ?: lastIssue.assignee
def lastAssigneeIndex = users.indexOf(lastAssignee)
def nextAssignee = users[(lastAssigneeIndex + 1) % users.size()]

issue.setAssignee(nextAssignee)
def changeHolder = new DefaultIssueChangeHolder()
originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, nextAssignee), changeHolder)

How it works:

  • The script still searches for the last assigned issue with type Certification
  • If there's no such issue, new issue is assigned to the first user in the project role
  • If there's such issue, assignee is based on the Original Assignee and Assignee field
    • if Original Assignee is empty, user from Assignee field is used
    • if Original Assignee it is not empty, user from Original Assignee is used
  • After issue creation both Assignee and Original Assignee fields are set
  • If you change Assignee field, Original Assignee is not affected (it stays the same)

Let's assume we have 4 users - Ben, Kristen, Miny, Mari

  • IssueA is created -> Assignee = Ben, Original Assignee = Ben
  • IssueB is created -> Assignee = Kristen, Original Assignee = Kristen
  • IssueB is manually assigned to Mari -> Assignee = Mari, Original Assignee = Kristen
  • IssueC is created -> Assignee = Miny, Original Assignee = Miny (because IssueB's Original Assigee is set to Kristen)

Hopefully I get it right now, please try.

- December 23, 2020

@Hana Kučerová I think we're finally all set! It works 100% the way we want it! I even hid the original assignee field from user's view (but still have it on create screen) and the script still works as expected! Thank you very much Hana, I'm beyond thankful you stuck this out with me all the way to the end :D.

Now just one last final question regarding the solution...I read through the script and I think I almost understand how it works. But as for the condition you set for lastIssueIdWithAssignee:

if (!lastIssueIdWithAssignee) {
issue.setAssignee(users.first())
def changeHolder = new DefaultIssueChangeHolder()
originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, users.first()), changeHolder)
return
}

and the part where the assignee and original assignee fields are set for the new issue:

issue.setAssignee(nextAssignee)
def changeHolder = new DefaultIssueChangeHolder()
originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, nextAssignee), changeHolder)

What exactly did you do in both blocks? How did you use the defaultIssueChangeHolder and ModifiedValue classes to make this work? Just out of curiosity.

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 31, 2020

Hi @Ian Balas ,

I'm really happy it works now!

These two blocks do the same thing - set the Original Assignee and Assignee fields...

  • the first case is, when there's no issue with assignee, we just use the first available user... 
  • in the second case we set the next assignee based on the found issue with assignee...

I used setCustomFieldValue originally, but it didn't work as I expected.

- December 31, 2020

@Hana Kučerová 

Ah, I see now how updateValue works instead of setCustomFieldValue. The value needs to get updated in data store, and not just specifically on the issue object. 

Okay, this all makes sense to me.

Well thank you very much for all the time you've put into this ticket! My team and I really appreciate your effort into this as you did't have to go all out like you did. We won't forget it!

Have a Happy New Year!

-Ian Balas

Hana Kučerová
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 1, 2021

@Ian Balas You're welcome :-) Happy New Year!

- May 11, 2021

Hi @Hana Kučerová ,

Hope all is well with you! I wanted to reach out to you again regarding a few slight changes that I want to make for the final version of the round robin auto-assignment script that you helped me with.

I wanted to make a copy of this post function script, set it for the 'Defect' issue type, and scan through the users with the 'Operations' and 'Utility' roles (rather than 'Workers'), still assigning in round-robin rotation, but now making it time-dependent. Basically I want 'Defect' issues to assign to 'Utility' users in round robin fashion from 4AM-4PM, and assign to 'Operations' users from 4PM-4AM. Is this at all possible by tweaking the script above in any way?

- June 7, 2021

@Hana Kučerová 

I was able to get this to work in another post.

See below:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.security.roles.ProjectRoleManager

import java.time.ZoneOffset

def issueManager = ComponentAccessor.issueManager

if (!!issue.assignee){
  return
} else {
  def utcHour = new Date().toInstant().atOffset( ZoneOffset.ofTotalSeconds(0)).hour
  if (utcHour >= 16 || utcHour < 4) {
    // The role you want assignees to set from
    final roleName = 'Utility'
    final originalAssigneeCustomFieldName = "customfield_20321"

    // If it is true, the assigned issues will be reassigned
    final reassignedIssues = true

    def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)
    def customFieldManager = ComponentAccessor.getCustomFieldManager()

    def originalAssigneeCustomField = customFieldManager.getCustomFieldObject(originalAssigneeCustomFieldName)

    // Get all of the users associated with the specified project role
    def projectRole = projectRoleManager.getProjectRole(roleName)

    // Sort the users of the project role using the user key
    def users = projectRoleManager.getProjectRoleActors(projectRole, issue.projectObject)
.applicationUsers
.toSorted { it.key }

    // There are no users in the specific project role
    if (!users) {
      log.info ("No users for project role $roleName")
      return
    }

    if (!reassignedIssues && issue.assignee) {
      log.info ('The issue is already assigned')
      return
    }

    // Find the latest created issue id that has an assignee
    def lastIssueIdWithAssignee = issueManager.getIssueIdsForProject(issue.projectObject.id)
.sort()
.reverse()
.find {
  def foundIssue = issueManager.getIssueObject(it)
  return foundIssue.assignee && foundIssue.issueType.name == "Defect"
  }

    // If no issue fulfilling above criteria is found, new cert issue is assigned to the first user in rr queue
    if (!lastIssueIdWithAssignee) {
      issue.setAssignee(users.first())
      def changeHolder = new DefaultIssueChangeHolder()
originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, users.first()), changeHolder)
      return
    }

// If issue is found, then assignee is based on the Original Assignee and Assignee fields:
// * assuming Original Assignee field is not empty, user in said field is taken, and issue assigns to
//the next user in queue
    def lastIssue = issueManager.getIssueObject(lastIssueIdWithAssignee)

    def lastAssignee = lastIssue.getCustomFieldValue(originalAssigneeCustomField) ?: lastIssue.assignee
    def lastAssigneeIndex = users.indexOf(lastAssignee)
    def nextAssignee = users[(lastAssigneeIndex + 1) % users.size()]

    issue.setAssignee(nextAssignee)
    def changeHolder = new DefaultIssueChangeHolder()
    originalAssigneeCustomField.updateValue(null, issue, new ModifiedValue(null, nextAssignee), changeHolder)
    }
  }

 

There's something else I was looking to add, though.

I want to enable the user to be able to manually set the assignee during issue creation, foregoing the round robin auto-assignment. But for the next issue created, if assignee is left blank, then pick right back up on the auto-assignment: set assignee as the next user after the user who the previous ticket was originally intended for before manual assignment.

For example, working again with the original 4 users (Ben M, Kristen B, Minyoung K, Mari L):

  • IssueA is created -> Assignee = Ben M, Original Assignee = Ben M

 

  • IssueB is created, BUT during creation, I assign to myself -> Assignee = Ian B, Original Assignee = Kristen [Kristen B would've been auto-assigned, but instead I assigned the issue to myself during creation. However, I still want Kristen B set as Original Assignee to maintain the order of the round robin.]

 

  • IssueC is created -> Assignee = Minyoung, Original Assignee = Minyoung (because IssueB's Original Assignee is set to Kristen EVEN THOUGH I assigned the ticket to myself in the end)

 

Is this at all possible?

TAGS
AUG Leaders

Atlassian Community Events