Hello,
I am developing a piece of code to take three inputs. This is then applied to a listener that looks at "Issue Created" or "Issue Updated" events.
The inputs are as follows:
Original Estimate (from the time tracking field)
Impact (Dropdown list with options "Low" and "High")
Severity (Dropdown list with three options "Minor", "Major" and "Critical")
The output is as follows:
Revised Estimate (Text (Single Line)).
What I want it to do:
Takes original estimate
Depending on value of Impact and Severity - Apply a frig factor
Output Estimate + impact and Severity hours to a field called "Revised Estimate"
What I've got working:
Original estimate is captured and then converted to a string (By changing the seconds value of Original estimate into h,m,d,m,y format). This works on its own. Yes I probably did this quite inefficently... But its the only way I could work out how to get this to work on one coffee :).
What I can't get to work:
Impact and Severity fields applying the frig factor and add them to "Revised Estimate"
I have pasted my code below. Please bear in mind, I am relatively inexperienced with Scriptrunner.
import com.atlassian.jira.component.ComponentAccessor import org.apache.log4j.Logger def log = Logger.getLogger("com.onresolve.scriptrunner.canned.jira.workflow.listeners.CustomListener") def customFieldManager = ComponentAccessor.getCustomFieldManager() def originalEstimateField = customFieldManager.getCustomFieldObjectByName('Original Estimate') def revisedEstimateField = customFieldManager.getCustomFieldObjectByName('Revised Estimate') def impactField = customFieldManager.getCustomFieldObjectByName('Impact') def severityField = customFieldManager.getCustomFieldObjectByName('Severity') def originalEstimate = event.issue.getOriginalEstimate() // Original Estimate in seconds def revisedEstimateStr = event.issue.getCustomFieldValue(revisedEstimateField) as String def impactOption = event.issue.getCustomFieldValue(impactField) def severityOption = event.issue.getCustomFieldValue(severityField) log.info("Original Estimate: ${originalEstimate}") log.info("Revised Estimate (String): ${revisedEstimateStr}") log.info("Impact Option: ${impactOption}") log.info("Severity Option: ${severityOption}") // Extract the actual values from the options def impact = impactOption ? impactOption.getValue() : null def severity = severityOption ? severityOption.getValue() : null log.info("Impact: ${impact}") log.info("Severity: ${severity}") // Convert revised estimate string to numeric value (seconds) def revisedEstimate = revisedEstimateStr ? revisedEstimateStr.toDouble() : 0 log.info("Revised Estimate (Numeric): ${revisedEstimate}") // Ensure original estimate is not null if (originalEstimate != null && impact != null && severity != null) { def impactCoefficient = 1.6 def severityCoefficient = 2.4 def additionalEstimate = (impactCoefficient * impact.toInteger() + severityCoefficient * severity.toInteger()) * 3600 // Convert hours to seconds def newRevisedEstimate = originalEstimate + additionalEstimate log.info("New Revised Estimate: ${newRevisedEstimate}") def issueManager = ComponentAccessor.getIssueManager() def mutableIssue = issueManager.getIssueObject(event.issue.id) mutableIssue.setCustomFieldValue(revisedEstimateField, formatTime(newRevisedEstimate)) issueManager.updateIssue(event.user, mutableIssue, com.atlassian.jira.event.type.EventDispatchOption.DO_NOT_DISPATCH, false) // Log the updated value def updatedRevisedEstimateStr = mutableIssue.getCustomFieldValue(revisedEstimateField) as String log.info("Updated Revised Estimate (String): ${updatedRevisedEstimateStr}") } else { log.warn("Original Estimate is null: ${originalEstimate == null}") log.warn("Impact is null: ${impact == null}") log.warn("Severity is null: ${severity == null}") } // Helper function to format time in seconds to human-readable format def formatTime(seconds) { def units = [ [unit: 'y', value: 60 * 60 * 24 * 365], [unit: 'm', value: 60 * 60 * 24 * 30], [unit: 'd', value: 60 * 60 * 24], [unit: 'h', value: 60 * 60], [unit: 'm', value: 60] ] def result = [] def remainingSeconds = seconds units.each { unit -> def unitValue = (remainingSeconds / unit.value).intValue() if (unitValue > 0) { result << "${unitValue}${unit.unit}" remainingSeconds -= unitValue * unit.value } } if (remainingSeconds > 0) { result << "${remainingSeconds}s" } return result.join(' ') }
Test Case:
Ticket created and Original Estimate is added (2hrs).
Ticket then processed through the workflow until "Impact" and "Severity" fields need to be set.
Result:
Remaining Estimate remains at "none"
What to the logs say:
2025-03-26T17:39:46,348 WARN [listeners.CustomListener]: Original Estimate is null: false
2025-03-26T17:39:46,348 WARN [listeners.CustomListener]: Impact is null: true
2025-03-26T17:39:46,348 WARN [listeners.CustomListener]: Severity is null: true
The script is clearly ignoring the "Impact" and "Severity" fields.
Any help would be appreciated.
Thanks
Hi @NSEU DevOps
It appears that there is something just not 100% correct and therefore some operation is failing.
You mentioned, this reacts on Issue Created and Issue Updated events. However, you have also mentioned that you passed the issue through the workflow. When is the custom field value set? Do you do it manually in the Edit screen?
Anyway: I would like to see the output of the listener first logging statements. They are set to Info, so they might not be shown. I have therefore adjusted the statements slightly (just for testing purposes and also added the event for debugging:
log.warn("Event: ${event.toString()}")
log.warn("Original Estimate: ${originalEstimate}") log.warn("Revised Estimate (String): ${revisedEstimateStr}") log.warn("Impact Option: ${impactOption}") log.warn("Severity Option: ${severityOption}")
Can you please let us know the output of this?
This might help us better understand the root cause here.
Thanks!
Stefan
Atlassian Government Cloud has achieved FedRAMP Authorization at the Moderate level! Join our webinar to learn how you can accelerate mission success and move work forward faster in cloud, all while ensuring your critical data is secure.
Register NowOnline forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.