Create asset with stock conditionally

This page is about Assets & Inventory Plugin for Jira DC. Using Cloud? Click here.

This document explains how to create a Jira post function using a Groovy script to conditionally create a new asset and set its stock level based on user input.

In this example, we will define a post function to create an asset and set it to the Assets asset custom field.

Functionality

The post function performs the following operations:

  • Checks if the asset custom field is empty. If it is not empty, does nothing.

  • Checks if In Stock is not Yes.

  • Creates asset with:

    • Asset Name field value will be the name of the asset.

    • Order Amount will be Number of items in stock asset value if the asset has this attribute.

  • Sets new asset to Assets custom field.

Script configuration

Fields of screen and asset

  • Assets - Asset custom field 

    • maps to assetCustomFieldId in Groovy script

Other fields

  • Asset Type - ListBox - Values are Asset Type Name - ID. 

    • Maps to assetTypeCustomFieldId in Groovy script

    • Examples:

      • Laptop - 3

      • Keyboard - 2

      • Computer - 27

  • Asset Name - Text Field: Name of the asset. If asset names are set to unique and the asset name is not unique for assets, it will not be created. 

    • Maps to assetNameCustomFieldId in Groovy script.

  • Order Amount - Number Field - number of items to be purchased

    • Maps to orderAmountCustomFieldId in Groovy script.

  • In Stock - Radio (Yes / No) - This must be No for this operation as we will create a new asset.

    • Maps to inStockCustomFieldId in Groovy script.

Asset attribute

  • Number of items in stock - Number - This will hold inventory of assets.

Add the post function

  1. In your Jira project settings, navigate to Workflows.

  2. Select the desired workflow and choose the transition where you want the script to run (for example, Create).

  3. Click on Post Functions.

  4. Add a new post function named [AIP] - Asset generic groovy script.

  5. Position the function after Issue Updated but before Update change history for an issue and store the issue in the database, GenerateChangeHistoryFunction, and Re-index post functions.

Post function parameters

Asset custom fields (with values) to inject as script parameter:  Select the target asset custom field.

Groovy script: 

import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.CustomFieldManager import com.atlassian.jira.issue.ModifiedValue import com.atlassian.jira.issue.customfields.option.Option import com.atlassian.jira.issue.util.DefaultIssueChangeHolder import groovy.json.JsonSlurper import org.apache.commons.lang3.StringUtils import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpGet import org.apache.http.client.methods.HttpPost import org.apache.http.entity.StringEntity import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.util.EntityUtils import org.apache.log4j.Level import org.apache.log4j.Logger // To test with script runner //import com.atlassian.jira.issue.Issue //def issueManager = ComponentAccessor.getIssueManager() //def Issue issue = issueManager.getIssueObject("KTP-25") Logger logger = Logger.getLogger("inventoryplugin.groovy.script") logger.setLevel(Level.DEBUG) /*** variables to configure **************************/ def assetCustomFieldId = 'customfield_10227'; def assetTypeCustomFieldId = 'customfield_10314'; def inStockCustomFieldId = 'customfield_10317'; def orderAmountCustomFieldId = 'customfield_10316'; def assetNameCustomFieldId = 'customfield_10318'; def inStockNoValue = 'No' def AUTH_HASH = 'Basic YWRtaW46YWRtaW4='; // auth hash for authorization. this sample is for user=admin, and password=admin def stockAttributeId = 67; // asset "Number of items in stock" attribute id /*****************************************************/ boolean isNotInStockCase(issueObj, inStockCustomFieldId, inStockNoValue) { def customFieldManager = ComponentAccessor.getCustomFieldManager() def cField = customFieldManager.getCustomFieldObject(inStockCustomFieldId) def option = (Option) issueObj.getCustomFieldValue(cField); return option != null && option.getValue() != null && option.getValue().equals(inStockNoValue) } def updateAssetCustomFieldOfTheIssue(issueObj, assetCustomFieldId, issueKey, assetCustomFieldValue) { // get issue def issueToUpdate = ComponentAccessor.getIssueManager().getIssueObject(issueKey); // get asset custom field CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager() def assetCf = customFieldManager.getCustomFieldObject(assetCustomFieldId) // asset cf id // execute update assetCf.updateValue(null, issueToUpdate, new ModifiedValue(issueToUpdate.getCustomFieldValue(assetCf), assetCustomFieldValue), new DefaultIssueChangeHolder()) } int getAssetTypeId(issueObj, assetTypeCustomFieldId) { def customFieldManager = ComponentAccessor.getCustomFieldManager() def cField = customFieldManager.getCustomFieldObject(assetTypeCustomFieldId) def assetTypeValue = issueObj.getCustomFieldValue(cField).getValue() as String return StringUtils.trim(StringUtils.substring(assetTypeValue, StringUtils.lastIndexOf(assetTypeValue, "- ") + 2)) as Integer; } /** * Checks if Asset custom field value is empty */ boolean isCustomFieldEmpty(issueObj, assetCustomFieldId) { def customFieldManager = ComponentAccessor.getCustomFieldManager() def cField = customFieldManager.getCustomFieldObject(assetCustomFieldId) return StringUtils.isBlank(issueObj.getCustomFieldValue(cField)); } /** * converts stream to json */ def streamToJson(stream) { BufferedReader br = new BufferedReader(new InputStreamReader(stream)); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line + "\n"); } br.close(); def jsonSlurper = new JsonSlurper() return jsonSlurper.parseText(sb.toString()) } /** * Calls GET service */ def callGetRestServiceAndReturnJson(serviceUrl, authHash) { Logger logger = Logger.getLogger("inventoryplugin.groovy.script") try { // get asset ID(s) def baseurl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl") CloseableHttpClient httpclient = HttpClients.createDefault(); def httpGet = new HttpGet(baseurl + serviceUrl); httpGet.setHeader("Accept", "application/json"); httpGet.setHeader("Content-type", "application/json"); httpGet.setHeader("Authorization", authHash); CloseableHttpResponse response = httpclient.execute(httpGet); try { if (response.getStatusLine().getStatusCode() == 200) { return streamToJson(response.getEntity().getContent()); } else { return null; } } finally { EntityUtils.consume(response.getEntity()); httpclient.close(); } } catch (Exception e) { logger.error("Error while calling service: " + e.getMessage() + serviceUrl); return null; } } /** * Calls POST service */ def callPostRestServiceAndReturnJson(serviceUrl, queryToPostJson, authHash) { Logger logger = Logger.getLogger("inventoryplugin.groovy.script") try { // get asset ID(s) def baseurl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl") CloseableHttpClient httpclient = HttpClients.createDefault(); def httpPost = new HttpPost(baseurl + serviceUrl); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-type", "application/json"); httpPost.setHeader("Authorization", authHash); def entity = new StringEntity(queryToPostJson); httpPost.setEntity(entity); CloseableHttpResponse response = httpclient.execute(httpPost); try { if (response.getStatusLine().getStatusCode() == 200) { return streamToJson(response.getEntity().getContent()) } else { return null; } } finally { EntityUtils.consume(response.getEntity()); httpclient.close(); } } catch (Exception e) { logger.error("Error while calling service:" + serviceUrl, e); return null; } } String getAssetName(issueObj, assetNameCustomFieldId) { def customFieldManager = ComponentAccessor.getCustomFieldManager() def cField = customFieldManager.getCustomFieldObject(assetNameCustomFieldId) return issueObj.getCustomFieldValue(cField); } int getOrderAmount(issueObj, orderAmountCustomFieldId) { def customFieldManager = ComponentAccessor.getCustomFieldManager() def cField = customFieldManager.getCustomFieldObject(orderAmountCustomFieldId) def orderAmount = issueObj.getCustomFieldValue(cField); return (orderAmount == null) ? 0 : orderAmount as Integer; } def createAsset(issueObj, assetTypeCustomFieldId, assetNameCustomFieldId, stockAttributeId, orderAmountCustomFieldId, authHash) { Logger logger = Logger.getLogger("inventoryplugin.groovy.script") int assetTypeId = getAssetTypeId(issueObj, assetTypeCustomFieldId); if (assetTypeId > 0) { def addStockAttr = false; def assetTypeJson = callGetRestServiceAndReturnJson("/rest/jip-api/1.0/form/" + assetTypeId + ".json", authHash); logger.debug('assetTypeJson') logger.debug(assetTypeJson) if (assetTypeJson == null || assetTypeJson.attributes == null) { return null; } // loop JsonSlurper assetTypeJson.attributes.each { if (it.attribute.id == stockAttributeId) { addStockAttr = true; } } // get asset ID(s) def assetName = getAssetName(issueObj, assetNameCustomFieldId) // this is the search query parameters. When you search assets on Asset Navigator, // same parameters appear on Developer tools-> Network Tab. You can make different searches and get the parameters from there when you need. def queryToPostJson = "{\n" + " \"formId\": " + assetTypeId + ",\n" + " \"name\": \"" + assetName + "\",\n"; // if asset type has stock attribute set it if (addStockAttr) { queryToPostJson += " \"attributes\": [ {\"attributeId\": " + stockAttributeId + ", \"attributeValue\": \"" + getOrderAmount(issueObj, orderAmountCustomFieldId) + "\"}] "; } else { queryToPostJson += " \"attributes\": [ ] "; } queryToPostJson += " }" logger.debug(queryToPostJson); def assetJson = callPostRestServiceAndReturnJson("/rest/jip-api/1.0/inventory.json", queryToPostJson, authHash) if (assetJson != null) { return assetJson.id; } else { return null; } } } if (isCustomFieldEmpty(issue, assetCustomFieldId)) { if (isNotInStockCase(issue, inStockCustomFieldId, inStockNoValue)) { Integer assetId = createAsset(issue, assetTypeCustomFieldId, assetNameCustomFieldId, stockAttributeId, orderAmountCustomFieldId, AUTH_HASH); if (assetId != null && assetId > 0) { updateAssetCustomFieldOfTheIssue(issue, assetCustomFieldId, issue.getKey(), "," + assetId + ",") return true; } } } return false;

Variables to set

Inspect the Issue Edit screen, locate the IDs of the fields, and then update the script.

def assetCustomFieldId = 'customfield_10227'; def assetTypeCustomFieldId = 'customfield_10314'; def inStockCustomFieldId = 'customfield_10317'; def orderAmountCustomFieldId = 'customfield_10316'; def assetNameCustomFieldId = 'customfield_10318'; def inStockNoValue = 'No' def AUTH_HASH = 'Basic YWRtaW46YWRtaW4='; // auth hash for authorization. this sample is for user=admin, and password=admin def stockAttributeId = 67; // asset "Number of items in stock" attribute id

Example: Post function location

Test

After the Done transition, a new asset will be created with inventory and assigned to the issue.

Before

After

Before

After

Â