Use Behaviour to enforce regex on text custom field

Jordan Nimlos September 16, 2020

I need to track expenses in a custom field. I would use Jira's native number-type field, but that allows for more than 2 decimal places, which would not be proper formatting for currency/expense values.  I want to ensure the values entered in this field are consistent so I can run some math operations (like summing across a collection of issues) on them using automation.  I'm hoping to find a way to use a text custom field and a Behaviour to enforce regex to allow only digits, no commas, and only two decimal places.

I've seen some examples of using regex with a scripted validator on the workflow, but none of them relating exactly to a currency format, plus I want to enforce it even on the Edit screen when there is no transition occurring, so for that reason a workflow validator won't suffice.

I've searched quite a bit for an answer to this assuming that someone else would have tried this exact thing before, but have not found a solution, so I apologize if this is an easy one and I just missed it in my searching :)

2 answers

1 accepted

Suggest an answer

Log in or Sign up to answer
3 votes
Answer accepted
Jordan Nimlos October 8, 2020

I'm not sure why I couldn't find a quick answer on this, but for anyone else who may struggle to figure it out in the future, it is actually simple (once you get the regex syntax correct for the matches() method):

def field = getFieldById(getFieldChanged())
def val = field.getValue() as String

if (!val.matches("[0-9]*|([0-9]*.[0-9]{2})")) {
field.setError("Enter either a whole number or up to 2 decimal places.")
} else {
field.clearError()
}

In my case the custom field is a text type, but I would guess this would work or could be adapted easily for a number-type field. 

Kelvin Teng December 9, 2020

Hi Jordan.

Thank you so much for posting the answer. I have tried using your script on a number field so it will only accepts integer numbers.

I thought I could modify it like the following but couldn't get it work. Would you have any idea why it won't work?

def field = getFieldById(getFieldChanged())
def val = field.getValue() as String

if (!val.matches("[0-9]*")) {
field.setError("Please enter integer values (i.e. can not be 4.1 etc.) only for Model Version")
} else {
field.clearError()
}
Jordan Nimlos December 9, 2020

@Kelvin Teng,

If you try logging the field value after converting to a String you'll quickly discover the problem with this.  Jira number fields are stored as Double, and when casting a Double to String, it is represented with a decimal.  That is just one problem.  If you get a large number (which you may not with something like model version), the converted string will be the scientific notation of the number.  I believe I ran into that with numbers in the tens of millions.  Basically, you're opening a whole can of worms when you try to regex a number value; what began as a very short script becomes a lot larger.

I did end up implementing this to use a number field for currency and ensure no one enters a number with more than 2 decimal places.  You might very well get away with a shortened version of this script if you can reasonably expect users to keep their numbers short.  See my finished script below:

def field = getFieldById(getFieldChanged())
def val = field.getValue() as String

// Only run input validation if user enters a value
if (val != null) {
// If user enters a whole number, casting the Double to a String will include a trailing ".O".  Remove this before checking.
if (val.matches("[0-9]*.0")) {
def index = val.indexOf(".")
val = val.substring(0,index)
}
// If user enters a large number, the value is represented in scientific notation. Convert this to a String representation of the non-scientific number before checking.   
if (val.matches(/[0-9]{1}\.[0-9]*E[0-9]{1,2}/)) {
        def index = val.indexOf("E") + 1 // Where is the E?
        def exp = val.substring(index, val.length()).asType(int) // Get the trailing exponent value from scientific format
        def num = val.substring(0,index - 1) // Get the numerical value preceding the E, example "1.23456789"
        def decIndex = num.indexOf(".") // Where is the decimal?
        num = num.substring(0, decIndex) + num.substring(decIndex + 1, num.length())

        if (exp + 1 >= num.length()) { // Catch large whole numbers ending in one or more 0s
        for (def i = 0; i < exp + 1 - num.length(); i++) {
            num += "0"
        }
        val = num
    } else { // Value includes a decimal.  Put it back in the right place for non-scientific format.
            StringBuffer newString = new StringBuffer(num) // Convert to StringBuffer class to make use of insert() method
        val = newString.insert(exp + 1, ".").toString() // Put the decimal where it belongs in non-scientific format, based on the exponent.
decIndex = val.indexOf(".") + 1 // Where is the decimal now?
def places = val.length() - decIndex // How many decimal places are represented?
            if (places < 2) { // If true, the last non-zero character the user entered was in the tenths place.  Add trailing 0s before passing val to the final regex check.
            while (places > 0) {
                    val += "0"
                    places--
                }
            }
        }
    }
    // Check if the formatted "val" string is a whole number or no more than 2 decimal places
    if (!val.matches("[0-9]*|([0-9]*.[0-9]{1,2})")) {
        field.setError("Enter either a whole number or up to 2 decimal places.")
    } else {
        field.clearError()
    }
}

Hope this helps you.

Like # people like this
0 votes
aashish gupta December 16, 2020

Hi @Jordan Nimlos

Thank you so much for the answer. I have tried using the script contain digit and one decimal and two decimal only. example 50, 50.5, 50.55 acceptable

but 50.555 not acceptable.

It giving XSRF Security token error while updating the issue.

image.png

Could you please help me?

 

def field = getFieldById(getFieldChanged())
def val = field.getValue() as String

if (!val.matches("[0-9]*(|[0-9]*.[0-9]{2})") && !val.matches("[0-9]*(|[0-9]*.[0-9]{1})") && !val.matches("[0-9]*")) {
field.setError("Enter either a whole number and 1 decimal or up to 2 decimal places.")
} else {
field.clearError()
}

 

Jordan Nimlos December 16, 2020

I don't think that XSRF Security Token warning is strictly related to your Behaviour script. The few times I've seen one of those come up, I just click Retry Operation and it works as expected. Have you tried clicking the button? Do you get the error every time? Try a new Jira session in a different browser and see if you get the same issue. Sorry, I don't have much advice beyond that.

Sysad April 29, 2021

@Jordan Nimlos how to prefix $ in regrex -----

if (!val.matches("[0-9]*(|[0-9]*.[0-9]{2})"))

 

any help is appreciated

Thanks

Sysad April 29, 2021

I don't see any error and still behavior won't work

This is what am using

def field = getFieldById(getFieldChanged())
def val = field.getValue() as String

if (!val.matches("\$[0-9].[0-9]{2})")) {
field.setError("Amount must be in dollar format.")
} else {
field.clearError()
}
Jordan Nimlos April 29, 2021

You'll have to be more specific about how it doesn't work.  Is your customfield a text field?  Is it allowing you to enter strings that don't include the $ dollar sign?  Some other problem?

From what you posted it looks like you're only allowing values that begin with a $, include a single digit before the decimal, and two decimal places, i.e. "$9.99" is an acceptable value but "$10.00" is not, nor is "9.99" or "$9".

You also might want to escape the "." because technically an unescaped period matches pretty much any character, so you'd want this instead:

\$[0-9]\.[0-9]{2}
Phil Evans July 14, 2021

@Jordan Nimlos cant get it to work too.

Text field. Behaviour on field. Mapping applied. What else could be wrong?

TAGS
AUG Leaders

Atlassian Community Events