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

Next challenges

Recent achievements

  • Global
  • Personal

Recognition

  • Give kudos
  • Received
  • Given

Leaderboard

  • Global

Trophy case

Kudos (beta program)

Kudos logo

You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.

View group

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Scriptrunner scripted field

Hello,

 I have a requirement where a custom field displays the sum of other 4 fields values. However if any of those four are blank/null then the total should remain blank/null but instead the field is still displaying the value of other fields which were not null. Below is the groovy script, kindly help.

 

import com.atlassian.jira.ComponentAccessor
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue;
//evaluate(new File("/path/to/your/script/root/HelloWorld.groovy")) // open and evaluate file (change the file path to your helper class)
def score9Field = 0
def score10Field = 0
def score11Field = 0
def score12Field = 0

IssueManager im = ComponentAccessor.getIssueManager()
CustomFieldManager cfm = ComponentAccessor.getCustomFieldManager()

//MJE 04-13-2020These need to stay null if null

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))!=null)
{ score9Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))} else {score9Field = null }

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Points"))!=null)
{ score10Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Points"))} else {score10Field = null }

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Auto Points"))!=null)
{ score11Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Auto Points"))} else {score11Field = null }

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Auto Points"))!=null)
{ score12Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Auto Points"))} else {score12Field = null }

//


double s9
if(score9Field!=null) { s9 = score9Field}
double s10
if(score10Field!=null) { s10 = score10Field}
double s11
if(score11Field!=null) { s11 = score11Field}
double s12
if(score12Field!=null) { s12 = score12Field}


CustomField lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")
double lclsum = 0
if(score9Field!=null && score10Field!=null &&score11Field!=null && score12Field!=null)
{lclsum = s9 + s10 + s11 + s12 }
else
{lclsum = double.NaN}

lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder());
return lclsum

 

Thank you.

4 answers

0 votes

I would shorten this a bit, to simplify it.

The first line I would change is

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))!=null)
{ score9Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))}

Leave out the "else", as you can just leave the score9Field as a zero for what I'm going to suggest.  

Repeat the same for the next three, no else's needed.

Then you can say

if ( (score9Field * score10Field * score11Field * score12Field) == 0 )

{lclsum = 0 }

else

{lclsum = s9 + s10 + s11 + s12 }

If that still doesn't work, at least the "System.out.println (score9Field)" type debugging lines won't have to be used in a large block of code

Hello @Nic Brough _Adaptavist_

 Thank you very much for your suggestion. I honestly don't know Groovy scripting. I've changed the code as per your suggestion, but it isn't working as expected. Below is the modified script, kindly correct me if I did it wrong.

 

import com.atlassian.jira.ComponentAccessor
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue;
//evaluate(new File("/path/to/your/script/root/HelloWorld.groovy")) // open and evaluate file (change the file path to your helper class)
def score9Field = 0
def score10Field = 0
def score11Field = 0
def score12Field = 0

IssueManager im = ComponentAccessor.getIssueManager()
CustomFieldManager cfm = ComponentAccessor.getCustomFieldManager()

//MJE 04-13-2020These need to stay null if null //

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))!=null)
{ score9Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Points"))}

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Points"))!=null)
{ score10Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Points"))}

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Auto Points"))!=null)
{ score11Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL RG Auto Points"))}

if(issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Auto Points"))!=null)
{ score12Field = (double)issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("LCL Wi-Fi Auto Points"))}

 


double s9
if(score9Field!=null) { s9 = score9Field}
double s10
if(score10Field!=null) { s10 = score10Field}
double s11
if(score11Field!=null) { s11 = score11Field}
double s12
if(score12Field!=null) { s12 = score12Field}


CustomField lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")
double lclsum = 0
if((score9Field * score10Field * score11Field * score12Field) == 0) {lclsum = 0 }
else
{lclsum = s9 + s10 + s11 + s12 }

lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder());
return lclsum

Hello,

 Could someone please help me with this script?

Thank you.

0 votes

How about compressing this to just a few lines like this:

import com.atlassian.jira.component.ComponentAccessor

def im = ComponentAccessor.getIssueManager()
def cfm = ComponentAccessor.getCustomFieldManager()

def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
   def cfs = cfm.getCustomFieldObjectsByName(fieldName)
   if(!cfs){
      log.warn "$fieldName was not found in custom fields"
      null
   } else {
      if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
issue.getCustomFieldValue(cfs.first())
}
}

if(fieldValues.any{it == null}){
return null
} else {
return fieldValues.sum{it.toInteger()}
}

Hello @Peter-Dave Sheehan ,

 Thank you very much for the response!

 Attached is the screenshot of the Error when I tried to replace the above code. Kindly suggest.

Thank you.Code Error.PNG

Did you try to run it with Preview?

The "Static Type checking" error can often be ignored. This just means that the code editor cannot confirm that the "toInteger() will work.

But if that really bothers you, you can change it to 

return fieldValues.sum{it as Integer}

Peter, you're right. The preview worked fine but the actual field in the jira issue isn't displaying 'null', the same issue I had with the actual code. PFA screenshot.Code Error1.PNG

I was making an assumption that these fields were "number fields".

Looks like they are text, and as such, they could contain "", " ", "ABC", "123" etc

So you need to protect for that:

import com.atlassian.jira.component.ComponentAccessor

def im = ComponentAccessor.getIssueManager()
def cfm = ComponentAccessor.getCustomFieldManager()

def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
   def cfs = cfm.getCustomFieldObjectsByName(fieldName)
   if(!cfs){
      log.warn "$fieldName was not found in custom fields"
      null
   } else {
      if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
def val = issue.getCustomFieldValue(cfs.first()) as String
(val.isInteger()) ? val.toInteger() : null
}
}

if(fieldValues.any{it == null}){
return null
} else {
return fieldValues.sum()
}

This part:

(val.isInteger()) ? val.toInteger() : null

Will ensure that is the value is not an integer, it is counted as null. This reads if val is an integer, convert val to integer or else return null.

I've added the above code, it is working perfectly in preview mode. But in the Jira issue, it doesn't display null nor making the sum of numbers. The return field 'LCL Total Points' is displaying nothing as shown below.Code Error2.PNG

Can you show the full screen of the scripted field?

There is something that doesn't quite jive.

A scripted field is not normally visible on the edit screen, it gets calculated live in the view screen and every time the issue is updated. It can never be edited by the user.

I suspect you have another field (perhaps with the same name) that's reflecting the sum correctly. Search for the issue in the issue navigator and add all the fields with the same name as new columns. 

This now explains why you had "lclscoreField.updateValue(...)" in your original script.

If you want the field to be calculated and editable at the same time, then the script needs to be in a behaviour configuration and with some special consideration for when the field should be re-calculated and refreshed (only when one of the other values change... the rest of the time, keep and accept the user's input).

Hello @Peter-Dave Sheehan

 Thank you very much for your excellent support!

 There are supposed to be two, the scripted one holds the script that writes to the other one that is shown on the screen. Below is the full screen of the scripted field.

 

Code Error3.PNG

Thank you.

I don't understand why you would choose to use 2 fields that way, but yeah, if you want to update a second field during the calculation of a scripted field, you can do that.

I would warn that this may not be very efficient in the long run. A scripted field is re-calculated upon each issue view (and during indexing). This is normally a read-only operation. By adding an update in the script, you are causing all read-operations to also generate a write operation. 

Sounds like what you really need is a script listener, listen for issue created and issue updated event, then just update the LCL Total Points field and delete the scripted field.

But if you really just wanted to add back the second field update to the script I suggested, you can modify the last block like this:

def lclsum
if(fieldValues.any{it == null}){
lclsum = null
} else {
lclsum = fieldValues.sum()
}

def lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")

lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder())
returm lclsum

I completely agree with you on not using the 2 fields. I've added the code as per your suggestions as below but it's still the same and the sum functionality isn't working. It shows 'null' for any values in the preview. In the issue screen, the field doesn't display 'null' and the sum functionality isn't working.

 

import com.atlassian.jira.component.ComponentAccessor

def im = ComponentAccessor.getIssueManager()
def cfm = ComponentAccessor.getCustomFieldManager()

def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
def cfs = cfm.getCustomFieldObjectsByName(fieldName)
if(!cfs){
log.warn "$fieldName was not found in custom fields"
null
} else {
if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
def val = issue.getCustomFieldValue(cfs.first()) as String
(val.isInteger()) ? val.toInteger() : null
}
}

def lclsum
if(fieldValues.any{it == null}){
lclsum = null
} else {
lclsum = fieldValues.sum()
}

def lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")

lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder())
returm lclsum

I forgot to include the imports necessary to support the customfield update

import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

And I had a typo in the last line. It should be "return" not "returm".

But if you want to use a listener as I suggested, this is what you need (similar to what was provided before, but with some indexing to make sure that the updates field can be searched on).

import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.issue.index.IssueIndexingService

def im = ComponentAccessor.issueManager
def cfm = ComponentAccessor.customFieldManager
def issue = event.issue as MutableIssue
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
def cfs = cfm.getCustomFieldObjectsByName(fieldName)
if(!cfs){
log.warn "$fieldName was not found in custom fields"
null
} else {
if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
def val = issue.getCustomFieldValue(cfs.first()) as String
(val.isInteger()) ? val.toInteger() : null
}
}

def lclsum
if(fieldValues.any{it == null}){
lclsum = null
} else {
lclsum = fieldValues.sum()
}

def lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")

issue.setCustomFieldValue(lclscoreField, lclsum)
im.updateIssue(user, issue, EventDispatchOption.DO_NOT_DISPATCH,false)
def wasIndexing = ImportUtils.isIndexIssues()
ImportUtils.setIndexIssues(true)
ComponentAccessor.getComponent(IssueIndexingService).reIndex(issue)
ImportUtils.setIndexIssues(wasIndexing)

@Peter-Dave Sheehan , I'm sorry if I'm wasting your valuable time. I've updated the code accordingly a shown below, but I still see 'null' for any values in the preview and the 'LCL Total Points' field is displaying blank in issue edit screen and is not showing up in issue view screen. 

I've also tried the listener code and it acts the same way as above.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

def im = ComponentAccessor.getIssueManager()
def cfm = ComponentAccessor.getCustomFieldManager()

def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
def cfs = cfm.getCustomFieldObjectsByName(fieldName)
if(!cfs){
log.warn "$fieldName was not found in custom fields"
null
} else {
if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
def val = issue.getCustomFieldValue(cfs.first()) as String
(val.isInteger()) ? val.toInteger() : null
}
}

def lclsum
if(fieldValues.any{it == null}){
lclsum = null
} else {
lclsum = fieldValues.sum()
}

def lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")

lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder())
return lclsum

Did you look at the logs when you executed the preview?

I had not been trying to test the code base those fields were no applicable in my environment, but to finally get to the bottom of it, I actually implemented it in my dev environment, and saw this in the log tab:

2020-05-28 10:39:47,217 ERROR [runner.ScriptFieldPreviewRunner]: Script field preview failed for field that has not yet been created java.lang.NullPointerException: Cannot invoke method isInteger() on null object at Script929$_run_closure1.doCall(Script929.groovy:19) at Script929.run(Script929.groovy:9)

Which I was able to quickly resolve by adding a single ? character to this line (just before isInteger):

(val?.isInteger()) ? val.toInteger() : null

  

I've added the above suggestion and still the same (doesn't calculate the fields and displays null for any values). I don't see any logs being captured with this code whereas I see logs being captured with my code(actual code) in place. Not sure, why is the code in your instance and not mine.

Code Error4.PNG

We can try to force the logging level and add some additional log messages:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue

def im = ComponentAccessor.getIssueManager()
def cfm = ComponentAccessor.getCustomFieldManager()
log.setLevel(org.apache.log4j.Level.DEBUG)
log.debug "Starting LCL Script"
def fieldsToSum = ['LCL RG Points','LCL Wi-Fi Points','LCL RG Auto Points','LCL Wi-Fi Auto Points']
def fieldValues = fieldsToSum.collect{ String fieldName ->
def cfs = cfm.getCustomFieldObjectsByName(fieldName)
if(!cfs){
log.warn "$fieldName was not found in custom fields"
null
} else {
if ( cfs.size()>1 ) {
log.warn "$fieldName matched ${cfs.size()} custom fields. Using the first one."
}
def val = issue.getCustomFieldValue(cfs.first()) as String
log.debug "Found $val in $fieldName, attempting to convert to integer: ${val?.isInteger()}"
(val?.isInteger()) ? val.toInteger() : null
}
}

def lclsum
if(fieldValues.any{it == null}){
log.warn "Found at least one null value in $fieldValues"
lclsum = null
} else {
lclsum = fieldValues.sum()
}

def lclscoreField = cfm.getCustomFieldObjectByName("LCL Total Points")
lclscoreField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(lclscoreField), lclsum), new DefaultIssueChangeHolder())
return lclsum

Awesome, below are the logs I see.

Code Error5.PNG

Ah... now we're getting somewhere.

So, the issue is with "isInteger() "  ... it says false, because the numbers are double.

So le'ts keep the values as double and sum them that way.

log.debug "Found $val in $fieldName, attempting to convert to number: ${val?.isNumber()}"
(val?.isNumber()) ? val.toDouble() : null

And because your custom field to update is a Text field, you will need

lclsum = fieldValues.sum()

If you want the final value to be "6" instead of "6.0", change this line

lclsum = fieldValues.sum().toInteger() as String

Now I see the sum functionality works perfect in preview and in issue. But, I don't see 'null' value being displayed in issue screen as shown below.

Code Error6.PNGCode Error7.PNG

That's normal. a null value shows a blank/empty in forms.

If you want to show the actual word null, then you must set the text value to 'null' as opposed to the logical null value

Okay that was my initial requirement and the limitation of my code. Could you please show me where do I change the text value to 'null' in order for the field 'LCL Total Points' to display it in the jira issue screen.

def lclsum
if(fieldValues.any{it == null}){
log.warn "Found at least one null value in $fieldValues"
lclsum = 'null'
} else {
lclsum = fieldValues.sum() as String
}

Thank you very much for all your help @Peter-Dave Sheehan 

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Published in Marketplace Apps & Integrations

Learn how to request and manage app requests in the Marketplace

G’day everyone! Super exciting news coming from the Marketplace. We have now fully rolled out the ability for end-users to submit app requests to admins directly from within the product! No longer ...

43 views 1 5
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you