Has anyone ever achieved this? I'm hoping for some Groovy examples of calculating billable hours for an issue. The one example I found is several years old (https://community.atlassian.com/t5/Jira-questions/Show-tempo-field-quot-Billed-quot-hours-in-jira-issues/qaq-p/71917) and seems to retrieve all logged time, regardless of whether it's billable or not.
Appreciate any insights!
Had the same requirement so I did invest some hours to figure out how. The following code allows you to get the attribute from Tempo called BILLABLE_SECONDS. I however noticed that when billable time (which is automatically copied from getTimeSpent during creation of the worklog) is not altered and kept identical, the code below will return a null for BILLABLE_SECONDS. So it seems that this value is not filled during creation if the value is the same as time spent. This is however NOT wat you would expect and something you do not want. Especially since when these values are not the same during creation or are altered and set to the same afterwards, the BILLABLE_SECONDS attribute does get set.
Current workaround that works for me: If the attribute BILLABLE_SECONDS returns null, set it to the same value as getTimeSpent() since this value is probably caused by the bug.
import org.apache.log4j.Levelimport org.apache.log4j.Logger
def log = Logger.getLogger("com.onresolve.scriptrunner.runner.CLITest")log.setLevel(Level.DEBUG)
import com.atlassian.jira.issue.Issue;import com.atlassian.jira.issue.worklog.Worklog;import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.runner.customisers.PluginModuleimport com.onresolve.scriptrunner.runner.customisers.WithPluginimport com.tempoplugin.core.workattribute.api.WorkAttributeValueServiceimport com.tempoplugin.core.workattribute.api.WorkAttributeService
@WithPlugin("is.origo.jira.tempo-plugin")
@PluginModuleWorkAttributeService workAttributeService
@PluginModuleWorkAttributeValueService workAttributeValueService
def issueManager = ComponentAccessor.getIssueManager()Issue issue = issueManager.getIssueObject(“PROJECTKEY-XXX")
for (Worklog worklog : ComponentAccessor.getWorklogManager().getByIssue(issue)) { def attributeBillableSeconds = workAttributeService.getWorkAttributeByKey("BILLABLE_SECONDS").returnedValue def workAttributeValue = workAttributeValueService.getWorkAttributeValueByWorklogAndWorkAttribute(worklog.id, attributeBillableSeconds.id) def billableseconds = workAttributeValue.returnedValue if (billableseconds == null) { billableseconds = worklog.getTimeSpent() } log.debug("Billableseconds: " + billableseconds?.value) log.debug("getTimespent: " + worklog.getTimeSpent())}
One of our developers solved this eventually, this looks like it may be similar to @Bram V's solution above. This requires Tempo Timesheets and a ScriptRunner Scripted Field.
import is.origo.jira.plugin.common.TempoWorklogManagerimport com.tempoplugin.core.datetime.api.TempoDateTimeimport com.onresolve.scriptrunner.runner.customisers.PluginModuleimport com.onresolve.scriptrunner.runner.customisers.WithPluginimport com.atlassian.jira.component.ComponentAccessorimport com.atlassian.jira.issue.Issueimport com.atlassian.jira.issue.worklog.Worklogimport com.atlassian.jira.bc.ServiceOutcomeimport com.tempoplugin.core.workattribute.api.WorkAttributeTypeimport com.tempoplugin.core.workattribute.api.WorkAttributeValueimport com.tempoplugin.core.workattribute.api.WorkAttributeServiceimport com.tempoplugin.core.workattribute.api.WorkAttributeValueService@WithPlugin("is.origo.jira.tempo-plugin")@PluginModuleTempoWorklogManager tempoWorklogManagerWorkAttributeService workAttributeService = tempoWorklogManager.getWorkAttributeService()WorkAttributeValueService valueService = tempoWorklogManager.getWorkAttributeValueService()long billableTime = 0long totalTimeSpent = 0ComponentAccessor.getWorklogManager().getByIssue(issue).each {Worklog it ->boolean hasValues = false;ServiceOutcome so = valueService.getWorkAttributeValuesByWorklogAndType(it.id, WorkAttributeType.Type.BILLABLE_SECONDS)so.returnedValue.each { WorkAttributeValue value ->hasValues = trueif (value != null && value.value != null) {billableTime += Long.parseLong(value.value)}}if (!hasValues) {billableTime += it.timeSpent}totalTimeSpent += it.timeSpent}billableTime
Hi Bram_V,
might be a solution that I can use too. At what point do you run that script then. When someone logs work?
Thank you for your feedback!
This example is something that you can just run at ScriptRunner Console. Personally we have a webhook setup that takes this billable time and syncs it towards out billing system. But you can use it in a lot of scripts, whenever you need to access the billable time.
We also have a scripted field that calculates the total billed time on an Epic for example.
thank you for your reply. Can you share that scripted field script to calculate the total billed time for an epic? Thanks!
thanks @Alex Fox, your solution is very effective.I still detect some inconsistencies, for example, if the worked/billed hours are edited it seems that some relationship is lost and the results are not exact.
Have you ever tried calling the function directly with the Tempo API? I don't know if this can be done, I don't have extensive programming knowledge
Thanks a lot for your quick response and help provided
It looks like you're new here. Sign in or register to get started.