---
title: "Create asset with stock conditionally"
canonical: "https://support.appfire.com/space/AIP/10256846/Create%20asset%20with%20stock%20conditionally"
format: markdown
---
> Macro (aura-html)

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: **

```groovy
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

![image](media://e517e6c8-5d8c-4a52-99d7-c0927d040ead)

## Test

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

| **Before** | **After** |
| --- | --- |
| ![image](media://5c16545f-3e5c-4f4d-ba67-9ea218aaa0f2) | ![image](media://1d878349-5c12-4378-8df4-673a9770b056) |