Dear atlassian community,
I got a custom field (text) named "Invoice Number" containing a number. The field should be filled on a certain workflow transition (post function). I need to get the highest previously used number so far in other issues of the same JIRA project and increase the invoice number of the current issue by 1.
Background: The idea is to generate some sort of sequential invoice number.
That's what I came up with so far. The first part that searches for the highest used invoice number works fine in script console, but when I add this script as a post function I get the following error:
Property 'results' not found
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.ComponentManager import com.atlassian.jira.jql.parser.JqlQueryParser import com.atlassian.jira.web.bean.PagerFilter 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.issue.ModifiedValue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.search.SearchProvider import com.atlassian.jira.issue.util.DefaultIssueChangeHolder; def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser) def searchProvider = ComponentAccessor.getComponent(SearchProvider) def issueManager = ComponentAccessor.getIssueManager() def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() // get all the used invoice numbers so far def query = jqlQueryParser.parseQuery("project = SJ AND 'Invoice Number' is not EMPTY ORDER BY 'Invoice Number' DESC") def results = searchProvider.search(query, user, PagerFilter.getUnlimitedFilter()) def i = 0 int newInvoiceNumber = 0 // get the highest used invoice number results.getIssues().each { documentIssue -> if (i == 0){ def issue = issueManager.getIssueObject(documentIssue.id) def customFieldManager = ComponentAccessor.getCustomFieldManager() CustomField cField = customFieldManager.getCustomFieldObjectByName("Invoice Number") int oldInvoiceNumber = issue.getCustomFieldValue(cField) as Integer // get the new invoice number newInvoiceNumber = ++oldInvoiceNumber } i++ } // set the new invoice number def customFieldManager = ComponentAccessor.getCustomFieldManager() def tgtField = customFieldManager.getCustomFieldObjects(issue).find {it.name == "Invoice Number"} DefaultIssueChangeHolder issueChangeHolder = new DefaultIssueChangeHolder(); tgtField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(tgtField), newInvoiceNumber),issueChangeHolder)
Do you guys have any ideas?
Thank you in advance, and kind regards!
Community moderators have prevented the ability to post new answers.
I've refactored as well, below. I think the above scripts are confusing the issues coming from the query with the issue involved in the transition.
Also you don't need an unlimited pager, if you are sorting by invoice number desc, then you only need the top one.
Note this will only work in a workflow function, not script console, as in script console there is no bound variable "issue".
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.jql.parser.JqlQueryParser import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.search.SearchProvider import org.apache.log4j.Logger MutableIssue issue //predifined variable into a script postfunction def log = Logger.getLogger("com.springtime.CustomScripts") // get all the used invoice numbers so far def query = ComponentAccessor.getComponent(JqlQueryParser).parseQuery("project = SJ AND 'Invoice Number' is not EMPTY ORDER BY 'Invoice Number' DESC") def results = ComponentAccessor.getComponent(SearchProvider).search( query, ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), new PagerFilter(1)) int maxInvoiceNumber = 0 CustomField cField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("Invoice Number") // get the highest used invoice number for(Issue documentIssue: results.getIssues()) { maxInvoiceNumber = documentIssue.getCustomFieldValue(cField) as Integer } // set the new invoice number issue.setCustomFieldValue(cField, maxInvoiceNumber + 1)
Hi Jamie,
thanks for your input. Just the last part "setCustomFieldValue" doesn't work yet. Seems like the issue is null:
java.lang.NullPointerException: Cannot invoke method setCustomFieldValue() on null object
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You need to remove this line:
MutableIssue issue //predifined variable into a script postfunction
It's masking the actual variable passed to the script, or you can look at it as if it's redefinining it to null.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Jamie, thanks I had to make one final addition. This is the working code now:
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.jql.parser.JqlQueryParser import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.search.SearchProvider // get all the used invoice numbers so far def query = ComponentAccessor.getComponent(JqlQueryParser).parseQuery("project = SJ AND 'Invoice Number' is not EMPTY ORDER BY 'Invoice Number' DESC") def results = ComponentAccessor.getComponent(SearchProvider).search(query,ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(),new PagerFilter(1)) int maxInvoiceNumber = 0 CustomField cField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("Invoice Number") // get the highest used invoice number for(Issue documentIssue: results.getIssues()) { maxInvoiceNumber = documentIssue.getCustomFieldValue(cField) as Integer } // set the new invoice number def newInvoiceNumber = ++maxInvoiceNumber as String issue.setCustomFieldValue(cField, newInvoiceNumber)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I believe it's possible for this solution to assign duplicate invoice numbers if for some reason the JIRA indexer is running slowly. JQL queries use the indexed field values to perform the search. If a new invoice issue hasn't been indexed, it will not be returned by the query, allowing a duplicate to be assigned.
Even if the indexer is running normally, it could assign a duplicate invoice numbers if two invoices are needed within milliseconds of each other.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Have you seen that happen? If so you could use a PropertySet on the current project, that retrieves and updates the counter more or less atomically.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I also think that this approach doesn't guarantee thread safety, as in there can be race condition. I didn't know about "PropertySet" , would look into it.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Jamie,
I have seen the indexer be behind enough to causes problems for a plugin using JQL for similar purposes. It doesn't happen often, but should be allowed for. Changing the script to use a PropertySet is safer.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If you just want a monotonically increasing number, how about just using the issue's id (issue.id) ? Or do you want a number that represents the actual number of invoices?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I refactored your script
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.jql.parser.JqlQueryParser import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.search.SearchProvider MutableIssue issue //predifined variable into a script postfunction def log = Logger.getLogger("com.springtime.CustomScripts") // get all the used invoice numbers so far def query = ComponentAccessor.getComponent(JqlQueryParser).parseQuery("project = SJ AND 'Invoice Number' is not EMPTY ORDER BY 'Invoice Number' DESC") def results = ComponentAccessor.getComponent(SearchProvider).search( query, ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), PagerFilter.getUnlimitedFilter()) int maxInvoiceNumber = 0 int curInvoiceNumber; CustomField cField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("Invoice Number") // get the highest used invoice number for(Issue documentIssue: results.getIssues()) { curInvoiceNumber = issue.getCustomFieldValue(cField) if(curInvoiceNumber > maxInvoiceNumber) maxInvoiceNumber = curInvoiceNumber } // set the new invoice number issue.setCustomFieldValue(cField, maxInvoiceNumber + 1)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello, thank you for your effort.
The mentioned error is now gone, but another error occurs:
java.lang.NullPointerException: Cannot invoke method getCustomFieldValue() on null object
Further the new number is not added to the custom field.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Here you are
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.jql.parser.JqlQueryParser import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.search.SearchProvider MutableIssue issue //predifined variable into a script postfunction def log = Logger.getLogger("com.springtime.CustomScripts") // get all the used invoice numbers so far def query = ComponentAccessor.getComponent(JqlQueryParser).parseQuery("project = SJ AND 'Invoice Number' is not EMPTY ORDER BY 'Invoice Number' DESC") def results = ComponentAccessor.getComponent(SearchProvider).search( query, ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), PagerFilter.getUnlimitedFilter()) int maxInvoiceNumber = 0 int curInvoiceNumber; CustomField cField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("Invoice Number") // get the highest used invoice number for(Issue documentIssue: results.getIssues()) { curInvoiceNumber = documentIssue.getCustomFieldValue(cField) if(curInvoiceNumber > maxInvoiceNumber) maxInvoiceNumber = curInvoiceNumber } // set the new invoice number issue.setCustomFieldValue(cField, maxInvoiceNumber + 1)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.