Errors when syncing resolution

Hello - We seem to have an ongoing problem where if the original Jira issue is closed with a specific resolution, then Exalate sync fails with the following error:

Could not perform a transition `{“transition”:{“id”:“41”}}` for work item with id `129,567`: Please select a Resolution.

We are syncing the resolution as far as I can tell but I am wondering if the screen on transition is causing issues. Any help would be appreciated.

Hi and welcome to the Exalate Community!

Thanks for sharing your question or insight, we appreciate your contribution.

A member of the Exalate team or community will review this post and respond as soon as possible.

In the meantime, feel free to explore related topics or join other conversations. We’re glad to have you here!

Hi @dkbt

Just to confirm my understanding: you have a sync configured between two Jira instances, and when a ticket is closed in one Jira, you want it to also close in the other. However, Exalate is failing to do this because the mandatory field “Resolution” is required, is that correct?

Could you please share the synchronization script from both sides so I can review it?

Thanks,
Sonal

Hi Sonal - It’s actually a sync between the same instance but I don’t think that matters but figured i’d let you know. Otherwise everything is true, when we close a Jira in the source project, it fails to transition the destination item because of the resolution.

Here are the scripts:

Outgoing

replica.key            = issue.key
replica.type           = issue.type
//replica.assignee       = issue.assignee
replica.reporter       = issue.reporter
replica.summary        = issue.summary
replica.description    = issue.description
replica.labels         = issue.labels
replica.comments       = issue.comments
replica.resolution     = issue.resolution
replica.status         = issue.status
replica.parentId       = issue.parentId
replica.priority       = issue.priority
replica.attachments    = issue.attachments
replica.project        = issue.project
replica.issueType      = issue.issueType
//replica.due            = issue.due
replica.resolved       = issue.resolved
/*
    Uncomment these lines if you want to send the full list of versions and components of the source project.
    replica.project.versions = []
    replica.project.components = []
*/

/*
    Custom Fields (CF)
      How to send any field value from the source side to the destination side.
        1/ Add the value to the replica object using the Display Name of the specific field.
        2/ Uncomment this next statement out and change accordingly:
        replica.customFields."CF Name" = issue.customFields."CF Name"
*/

//for bug only
if (issue.type.name == "Bug") {
    replica.customFields."Total Clients Reporting" = issue.customFields."10305"
    replica.customFields."Total Associated ARR" = issue.customFields."10306"
    replica.customFields."Severity" = issue.customFields."10049"
}

//Added on 17 Jan 2025
if(issue.type.name == "Story" || issue.type.name == "Task" || issue.type.name == "Bug"){
    replica.customFields."Allocation" = issue.customFields."10168"
}

replica.customFields."Product" = issue.customFields."10165"
replica.customFields."Team" = issue.customFields."10170"
replica.customFields."Salesforce - Record Owner" = issue.customFields."10472"

//v8 backend edits only
if (issue.type.name == "VEdits") {
    replica.customFields."Sub Issue Type" = issue.customFields."10269"
}

// Exalate API Reference Documentation: https://docs.exalate.com/docs/exalate-api-reference-documentation


Incoming

if (firstSync && replica.project.key == "CLONEEVA") {
    issue.projectKey   = "EVA"
    issue.typeName     = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
if (firstSync && replica.project.key == "EVA") {
    issue.projectKey   = "CLONEEVA"
    issue.typeName     = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
issue.summary      = replica.summary
issue.description  = replica.description
issue.comments     = commentHelper.mergeComments(issue, replica)
issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)
issue.labels       = replica.labels
issue.status       = replica.status.name
//issue.priority     = replica.priority
//issue.due          = replica.due

def tempUser = nodeHelper.getUser(replica.reporter?.email)
if ((issue.status.name == "Canceled" || replica.status.name == "Canceled") && 
  (replica.issueType.name == "Bug" || issue.issueType?.name == "Bug" || 
  replica.issueType.name == "Story" || issue.issueType?.name == "Story")) {
  def resolutionMap = [
    "Done" : "Done",
    "Won\'t Do" : "Won\'t Do",
    "Duplicate" :  "Duplicate",
    "Declined" :  "Declined",
    "Canceled" :  "Canceled",
    "Cannot Reproduce" :  "Cannot Reproduce",
    "Not A Bug" :  "Not A Bug" ]

  def targetResolutionName = resolutionMap[replica.resolution?.name] ?: "Done"
  workflowHelper.transition(issue, replica.status.name, tempUser)
  issue.resolution = nodeHelper.getResolution(targetResolutionName)
}

if(replica.issueType.name == "Bug"){
  issue.customFields."10305".value = replica.customFields."Total Clients Reporting"?.value
  issue.customFields."10306".value = replica.customFields."Total Associated ARR"?.value
  issue.customFields."10049".value = replica.customFields."Severity"?.value?.value
  issue.resolved = replica.resolved
}

//populate remote jira key
if(issue.projectKey == "CLONEEVA" && (replica.issueType.name == "V8 Backend Edits" || replica.issueType.name == "Story" || replica.issueType.name == "Task" || replica.issueType.name == "Bug")){
 issue.customFields."10505".value = replica.key
}

if(replica.issueType.name == "V8 Backend Edits"){
  issue.customFields."10269".value = replica.customFields."Sub Issue Type"?.value?.value ?: "Root Folder"
}

//Allocation checks added on 17 Jan 2025
if(replica.issueType.name == "Story" || replica.issueType.name == "Task" || replica.issueType.name == "Bug"){
  issue.customFields."10168".value = replica.customFields."Allocation"?.value?.value
}

//Multi-select pickliest
issue.customFields."10165".value = nodeHelper.getOptions(issue, 10165,
         replica.customFields."Product".value.collect {it -> it.value})
issue.customFields."10170".value = replica.customFields."Team"?.value?.value

/*
    User Synchronization (Assignee/Reporter)
      Set a Reporter/Assignee from the source side. If the user can't be found, set a default user.
        You can also use this approach for custom fields of the type User
*/
          def defaultUser = nodeHelper.getUserByEmail("jira_randdautomationsapi@scrubbed.com")
          issue.reporter  = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser 
          //issue.assignee  = nodeHelper.getUserByEmail(replica.assignee?.email)
          issue.customFields."10472".value = replica.customFields."Salesforce - Record Owner"?.value


/*
    Comment Synchronization
      Impersonate comments with the original author. The sync will work if the user already exists
      in the local instance.
      Note: Don’t forget to remove the original comments sync line if you are using this approach.
      issue.comments = commentHelper.mergeComments(issue, replica) {
        it.executor = nodeHelper.getUserByEmail(it.author?.email)
      }
*/

issue.issueType    = replica.issueType

// Exalate API Reference Documentation: https://docs.exalate.com/docs/exalate-api-reference-documentation

i think in outgoing sync script you need to use

replica.resolution = issue.resolution

instead of

replica.resolved       = issue.resolved

Here is the documentation you can refer:

Thanks,

Sonal

Thank you. I will update my script and see if that helps

Looks like that did not solve the problem. I tested and got the same error. It doesn’t seem to matter if it is the local work or remote work item that is transitioned

Can you please share your updated script again?

replica.key            = issue.key
replica.type           = issue.type
//replica.assignee       = issue.assignee
replica.reporter       = issue.reporter
replica.summary        = issue.summary
replica.description    = issue.description
replica.labels         = issue.labels
replica.comments       = issue.comments
replica.resolution     = issue.resolution
replica.status         = issue.status
replica.parentId       = issue.parentId
replica.priority       = issue.priority
replica.attachments    = issue.attachments
replica.project        = issue.project
replica.issueType      = issue.issueType
//replica.due            = issue.due
replica.resolution = issue.resolution
/*
    Uncomment these lines if you want to send the full list of versions and components of the source project.
    replica.project.versions = []
    replica.project.components = []
*/

/*
    Custom Fields (CF)
      How to send any field value from the source side to the destination side.
        1/ Add the value to the replica object using the Display Name of the specific field.
        2/ Uncomment this next statement out and change accordingly:
        replica.customFields."CF Name" = issue.customFields."CF Name"
*/

//for bug only
if (issue.type.name == "Bug") {
    replica.customFields."Total Clients Reporting" = issue.customFields."10305"
    replica.customFields."Total Associated ARR" = issue.customFields."10306"
    replica.customFields."Severity" = issue.customFields."10049"
}

//Added on 17 Jan 2025
if(issue.type.name == "Story" || issue.type.name == "Task" || issue.type.name == "Bug"){
    replica.customFields."Allocation" = issue.customFields."10168"
}

replica.customFields."Product" = issue.customFields."10165"
replica.customFields."Team" = issue.customFields."10170"
replica.customFields."Salesforce - Record Owner" = issue.customFields."10472"

//v8 backend edits only
if (issue.type.name == "V8 Backend Edits") {
    replica.customFields."Sub Issue Type" = issue.customFields."10269"
}

// Exalate API Reference Documentation: https://docs.exalate.com/docs/exalate-api-reference-documentation


if (firstSync && replica.project.key == "CLONEEVA") {
    issue.projectKey   = "EVA"
    issue.typeName     = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
if (firstSync && replica.project.key == "EVA") {
    issue.projectKey   = "CLONEEVA"
    issue.typeName     = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
issue.summary      = replica.summary
issue.description  = replica.description
issue.comments     = commentHelper.mergeComments(issue, replica)
issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)
issue.labels       = replica.labels
issue.status       = replica.status.name
//issue.priority     = replica.priority
//issue.due          = replica.due

def tempUser = nodeHelper.getUser(replica.reporter?.email)
if ((issue.status.name == "Canceled" || replica.status.name == "Canceled") && 
  (replica.issueType.name == "Bug" || issue.issueType?.name == "Bug" || 
  replica.issueType.name == "Story" || issue.issueType?.name == "Story")) {
  def resolutionMap = [
    "Done" : "Done",
    "Won\'t Do" : "Won\'t Do",
    "Duplicate" :  "Duplicate",
    "Declined" :  "Declined",
    "Canceled" :  "Canceled",
    "Cannot Reproduce" :  "Cannot Reproduce",
    "Not A Bug" :  "Not A Bug" ]

  def targetResolutionName = resolutionMap[replica.resolution?.name] ?: "Done"
  workflowHelper.transition(issue, replica.status.name, tempUser)
  issue.resolution = nodeHelper.getResolution(targetResolutionName)
}

if(replica.issueType.name == "Bug"){
  issue.customFields."10305".value = replica.customFields."Total Clients Reporting"?.value
  issue.customFields."10306".value = replica.customFields."Total Associated ARR"?.value
  issue.customFields."10049".value = replica.customFields."Severity"?.value?.value
  issue.resolved = replica.resolved
}

//populate remote jira key
if(issue.projectKey == "CLONEEVA" && (replica.issueType.name == "V8 Backend Edits" || replica.issueType.name == "Story" || replica.issueType.name == "Task" || replica.issueType.name == "Bug")){
 issue.customFields."10505".value = replica.key
}

if(replica.issueType.name == "V8 Backend Edits"){
  issue.customFields."10269".value = replica.customFields."Sub Issue Type"?.value?.value ?: "Root Folder"
}

//Allocation checks added on 17 Jan 2025
if(replica.issueType.name == "Story" || replica.issueType.name == "Task" || replica.issueType.name == "Bug"){
  issue.customFields."10168".value = replica.customFields."Allocation"?.value?.value
}

//Multi-select pickliest
issue.customFields."10165".value = nodeHelper.getOptions(issue, 10165,
         replica.customFields."Product".value.collect {it -> it.value})
issue.customFields."10170".value = replica.customFields."Team"?.value?.value

/*
    User Synchronization (Assignee/Reporter)
      Set a Reporter/Assignee from the source side. If the user can't be found, set a default user.
        You can also use this approach for custom fields of the type User
*/
          def defaultUser = nodeHelper.getUserByEmail("jira_randdautomationsapi@scrubbed.com")
          issue.reporter  = nodeHelper.getUserByEmail(replica.reporter?.email) ?: defaultUser 
          //issue.assignee  = nodeHelper.getUserByEmail(replica.assignee?.email)
          issue.customFields."10472".value = replica.customFields."Salesforce - Record Owner"?.value


/*
    Comment Synchronization
      Impersonate comments with the original author. The sync will work if the user already exists
      in the local instance.
      Note: Don’t forget to remove the original comments sync line if you are using this approach.
      issue.comments = commentHelper.mergeComments(issue, replica) {
        it.executor = nodeHelper.getUserByEmail(it.author?.email)
      }
*/

issue.issueType    = replica.issueType

// Exalate API Reference Documentation: https://docs.exalate.com/docs/exalate-api-reference-documentation