Skip to end of banner
Go to start of banner

Search assets with Java API

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

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

This example searches for assets with asset type All types and asset IDs between 10494 and 10495, and then extracts asset field values, which contain core, system, and attribute values.

If you would like to override security, use aipIndexService.queryDocsForObjectOverrideSecurity, and if you want permission to check with the current user, use aipIndexService.queryDocsForObject.

Prerequisites

  • Familiarity with Java and Groovy programming languages

  • Understanding of JSM API

Filter out assets at search

Navigate to Asset Navigator and perform the search. You can select multiple options to filter assets.

Script

 Click to see the script:
import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import groovy.transform.Field
import inventoryplugin.service.status.StatusEnum
import inventoryplugin.service.status.SubstatusEnum
import inventoryplugin.dto.portable.InventoryDto
import inventoryplugin.dto.portable.InventoryItemDto
import inventoryplugin.helper.JipUtilityHelper
import inventoryplugin.service.index.AipIndexService
import inventoryplugin.service.index.search.QueryIndexParam
import inventoryplugin.service.index.search.QueryIndexResult
import inventoryplugin.service.index.search.QueryIndexSearchParam
import inventoryplugin.service.rest.dto.SystemFieldDto
import org.apache.log4j.Level
import org.apache.log4j.Logger

@Field Logger logger = Logger.getLogger("snapbytes.inventoryplugin.groovy.script")
logger.setLevel(Level.DEBUG)

@WithPlugin("plugin.jip")

@PluginModule
AipIndexService aipIndexService

try {
    QueryIndexParam queryIndexParam = new QueryIndexParam();
    queryIndexParam.setQuerySource(QueryIndexParam.QuerySource.GENERIC_SEARCH);
    queryIndexParam.setSortField("asset.name");
    queryIndexParam.setSortDirection("asc");
    queryIndexParam.setSortType("STRING");
    queryIndexParam.setSearchType("basic");
    queryIndexParam.setPageNumber(1);
    queryIndexParam.setPageSize(1000);
    queryIndexParam.setQueryIndexSearchParams(new ArrayList<>());

    // filter by asset type
    QueryIndexSearchParam query = new QueryIndexSearchParam();
    query.setField("form.name");
    query.setKeywords(new ArrayList<>());
    query.getKeywords().add("all types");
    query.setRange(true);
    queryIndexParam.getQueryIndexSearchParams().add(query);

    QueryIndexSearchParam query2 = new QueryIndexSearchParam();
    query2.setField("asset.id");
    query2.setMinNum(10494);
    query2.setMaxNum(10495);
    query2.setRange(true);
    queryIndexParam.getQueryIndexSearchParams().add(query2);

    def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
    //QueryIndexResult queryIndexResult = aipIndexService.queryDocsForObject(queryIndexParam, user);
    QueryIndexResult queryIndexResult = aipIndexService.queryDocsForObjectOverrideSecurity(queryIndexParam, user);
    def queryIndexResultAsTxt = JipUtilityHelper.convertObjectToJsonStringSafe(queryIndexResult);

    List<InventoryDto> assets = JipUtilityHelper.convertJsonStringToListSafe(queryIndexResult.getAssets(), InventoryDto.class);

    if (assets != null) {
        logger.debug(assets.size() + ' assets found')

        for (InventoryDto oneAsset : assets) {
            logger.debug('----------------------------------------------------')
            logger.debug('----------------------------------------------------')
            logger.debug('Asset id: ' + oneAsset.id)
            logger.debug('Asset name: ' + oneAsset.name)
            logger.debug('Asset type id: ' + oneAsset.formId)
            logger.debug('Asset type name: ' + oneAsset.formName)
            logger.debug('Asset type scheme id: ' + oneAsset.objectSchemaId)
            logger.debug('Asset type scheme name: ' + oneAsset.objectSchemaName)
            logger.debug('Attachments json string: ' + oneAsset.attachments)
            logger.debug('Updated by: ' + oneAsset.updatedBy)
            logger.debug('Updated at: ' + oneAsset.updated)
            logger.debug('Created by: ' + oneAsset.creator)
            logger.debug('Created at: ' + oneAsset.created)

            logger.debug('--- Attributes ----------------------')
            List<InventoryItemDto> inventoryItems = oneAsset.getInventoryItems();
            for (InventoryItemDto assetAttr : inventoryItems) {

                if (assetAttr.attributeType == 'InventoryList' || assetAttr.attributeType == 'InventoryListByForm') {
                    logger.debug(assetAttr.attributeName + ': ');
                    for (def oneVal : assetAttr.inventoryRefs) {
                        logger.debug('       - id:' + oneVal.id + ', name:' + oneVal.name);
                    }
                } else if (assetAttr.attributeType == 'DatePicker' || assetAttr.attributeType == 'DatetimePicker') {
                    logger.debug(assetAttr.attributeName + ': ' + assetAttr.isoDateValue);
                } else if (assetAttr.attributeType == 'JiraMultipleUsers') {
                    logger.debug(assetAttr.attributeName + ': ');
                    if (assetAttr.users != null && assetAttr.users.size() > 0) {
                        for (def oneUser : assetAttr.users) {
                            logger.debug("     ----- " );
                            logger.debug("     - name: " + oneUser.name);
                            logger.debug("     - key: " + oneUser.key);
                            logger.debug("     - display name: " + oneUser.displayName);
                            logger.debug("     - email address: " + oneUser.emailAddress);
                            logger.debug("     - active: " + oneUser.active);
                        }
                    }
                } else if (assetAttr.attributeType == 'JiraSingleUser' || assetAttr.attributeType == 'UserPicker') {
                    logger.debug(assetAttr.attributeName + ': ');
                    if (assetAttr.user != null) {
                        logger.debug("     - name: " + assetAttr.user.name);
                        logger.debug("     - key: " + assetAttr.user.key);
                        logger.debug("     - display name: " + assetAttr.user.displayName);
                        logger.debug("     - email address: " + assetAttr.user.emailAddress);
                        logger.debug("     - active: " + assetAttr.user.active);
                    }
                } else if (assetAttr.attributeType == 'URL') {
                    logger.debug(assetAttr.attributeName + ': ' + assetAttr.value);
                } else if (assetAttr.multiListValues != null && assetAttr.multiListValues.size() > 0) {
                    logger.debug(assetAttr.attributeName + ': ');
                    for (def oneVal : assetAttr.multiListValues) {
                        logger.debug('       - ' + oneVal);
                    }
                } else {
                    String displayValue = (assetAttr.displayValue != null && assetAttr.displayValue != assetAttr.value) ? (", (Display value: " + assetAttr.displayValue + ")") : ""
                    logger.debug(assetAttr.attributeName + ': ' + assetAttr.value + displayValue);
                }
            }

            logger.debug('--- System Fields ----------------------')
            List<SystemFieldDto> systemFields = oneAsset.getSystemFields();
            for (SystemFieldDto systemFieldDto : systemFields) {

                if (systemFieldDto.fieldName == "asset.status") {
                    def status = StatusEnum.getByKey(systemFieldDto.value);
                    String displayValue = status != null ? status.displayName : ""
                    logger.debug(systemFieldDto.fieldName + ': ' + systemFieldDto.value + " (Display value: " + displayValue + ")");
                } else if (systemFieldDto.fieldName == "asset.substatus") {
                    def substatus = SubstatusEnum.getByKey(systemFieldDto.value);
                    String displayValue = substatus != null ? substatus.displayName : ""
                    logger.debug(systemFieldDto.fieldName + ': ' + systemFieldDto.value + " (Display value: " + displayValue + ")");
                } else {
                    logger.debug(systemFieldDto.fieldName + ': ' + systemFieldDto.value);
                }
            }
        }
    }
    return queryIndexResultAsTxt;

} catch (Exception e) {
    logger.debug(e.getMessage())
    return e.getMessage();
}

Output

 Click to see the output:
2020-06-23 17:37:28,849 DEBUG [groovy.script]: 2 assets found
2020-06-23 17:37:28,849 DEBUG [groovy.script]: ----------------------------------------------------
2020-06-23 17:37:28,850 DEBUG [groovy.script]: ----------------------------------------------------
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset id: 10494
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset name: all two - clone 3
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset type id: 36
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset type name: all types
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset type scheme id: 2
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Asset type scheme name: HR Assets
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Attachments json string: [ {
  "originalFileName" : "marla.jpg",
  "serverFileName" : "10653_77560_1591789643914.jpg",
  "fileSize" : "24 KB",
  "created" : "10/Jun/20 2:47 PM",
  "creator" : "admin"
} ]
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Updated by: admin
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Updated at: Tue Jun 23 17:24:06 MSK 2020
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Created by: admin
2020-06-23 17:37:28,850 DEBUG [groovy.script]: Created at: Wed Jun 10 14:37:02 MSK 2020
2020-06-23 17:37:28,850 DEBUG [groovy.script]: --- Attributes ----------------------
2020-06-23 17:37:28,851 DEBUG [groovy.script]: all - Asset list: 
2020-06-23 17:37:28,851 DEBUG [groovy.script]:        - id:183, name:admin çalışanı
2020-06-23 17:37:28,851 DEBUG [groovy.script]:        - id:221, name:Asset Name 0001
2020-06-23 17:37:28,851 DEBUG [groovy.script]: all - asset list by type: 
2020-06-23 17:37:28,851 DEBUG [groovy.script]:        - id:10379, name:blue pc
2020-06-23 17:37:28,851 DEBUG [groovy.script]:        - id:138, name:Emp 001
2020-06-23 17:37:28,851 DEBUG [groovy.script]: all - checkbox: 
2020-06-23 17:37:28,852 DEBUG [groovy.script]:        - ada
2020-06-23 17:37:28,852 DEBUG [groovy.script]:        - bal
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - date picker: 2019-12-24
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - date time picker: 2019-12-31T14:39
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - dropdown list: cam
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - IP: 192.168.1.1
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - IPv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
2020-06-23 17:37:28,852 DEBUG [groovy.script]: All - Jira user: 
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - name: tyler
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - key: tyler
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - display name: Tyler Durden
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - email address: tyler@example.com
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - jira single user: 
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - name: agrant-sd-demo
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - key: agrant-sd-demo
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - display name: Alana Grant
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - email address: agrant-sd-demo@example.com
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,852 DEBUG [groovy.script]: all - jira multiple users: 
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      ----- 
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - name: mdavis-sd-demo
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - key: mdavis-sd-demo
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - display name: Mitch Davis
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - email address: mdavis-sd-demo@example.com
2020-06-23 17:37:28,852 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      ----- 
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      - name: assetuser
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      - key: assetuser
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      - display name: Asset User 1
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      - email address: asset-user@example.com
2020-06-23 17:37:28,853 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - listbox: lahmacun
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - listbox multiple: 
2020-06-23 17:37:28,853 DEBUG [groovy.script]:        - mavi
2020-06-23 17:37:28,853 DEBUG [groovy.script]:        - kırmızı
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - number long: 120
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - number float: 150
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - radio: paranoid
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - text: sample text here
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - text area: more sample 
text here
2020-06-23 17:37:28,853 DEBUG [groovy.script]: all - url: https://jira-test.snapbytes.com/
2020-06-23 17:37:28,853 DEBUG [groovy.script]: --- System Fields ----------------------
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.quantity: 100
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.reservedFor: vwong-sd-demo
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.costCurrency: USD
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.assetTag: asset tag
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.owner: agrant-sd-demo
2020-06-23 17:37:28,854 DEBUG [groovy.script]: asset.serialNumber: my serial 101
2020-06-23 17:37:28,855 DEBUG [groovy.script]: asset.status: 1 (Display value: In stock)
2020-06-23 17:37:28,855 DEBUG [groovy.script]: asset.installDate: 2020-06-10
2020-06-23 17:37:28,855 DEBUG [groovy.script]: asset.managedBy: mdavis-sd-demo
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.substatus: 2 (Display value: Reserved)
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.assignee: rlee-sd-demo
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.belongsToGroup: [ "jira-servicedesk-users", "jira-software-users" ]
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.cost: 1200
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.location: 2
2020-06-23 17:37:28,856 DEBUG [groovy.script]: asset.invoiceNumber: my invoice 10387
2020-06-23 17:37:28,857 DEBUG [groovy.script]: ----------------------------------------------------
2020-06-23 17:37:28,857 DEBUG [groovy.script]: ----------------------------------------------------
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset id: 10495
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset name: all two - clone 4
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset type id: 36
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset type name: all types
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset type scheme id: 2
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Asset type scheme name: HR Assets
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Attachments json string: [ {
  "originalFileName" : "Marla_Singer_2.jpg",
  "serverFileName" : "10163_48190_1591789628314.jpg",
  "fileSize" : "27 KB",
  "created" : "10/Jun/20 2:47 PM",
  "creator" : "admin"
}, {
  "originalFileName" : "xps13-2.jpg",
  "serverFileName" : "23815_70783_1591870901186.jpg",
  "fileSize" : "42 KB",
  "created" : "11/Jun/20 1:21 PM",
  "creator" : "admin"
}, {
  "originalFileName" : "bobswift example2.groovy",
  "serverFileName" : "06040_73967_1592483691399.groovy",
  "fileSize" : "3 KB",
  "created" : "18/Jun/20 3:34 PM",
  "creator" : "admin"
} ]
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Updated by: admin
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Updated at: Tue Jun 23 17:24:24 MSK 2020
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Created by: admin
2020-06-23 17:37:28,857 DEBUG [groovy.script]: Created at: Wed Jun 10 14:37:31 MSK 2020
2020-06-23 17:37:28,857 DEBUG [groovy.script]: --- Attributes ----------------------
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - Asset list: 
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - id:183, name:admin çalışanı
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - id:221, name:Asset Name 0001
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - asset list by type: 
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - id:10379, name:blue pc
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - id:138, name:Emp 001
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - checkbox: 
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - ada
2020-06-23 17:37:28,857 DEBUG [groovy.script]:        - bal
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - date picker: 2019-12-26
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - date time picker: 2019-12-31T14:39
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - dropdown list: cam
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - IP: 192.168.1.1
2020-06-23 17:37:28,857 DEBUG [groovy.script]: all - IPv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
2020-06-23 17:37:28,857 DEBUG [groovy.script]: All - Jira user: 
2020-06-23 17:37:28,857 DEBUG [groovy.script]:      - name: tyler
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - key: tyler
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - display name: Tyler Durden
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - email address: tyler@example.com
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - jira single user: 
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - name: agrant-sd-demo
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - key: agrant-sd-demo
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - display name: Alana Grant
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - email address: agrant-sd-demo@example.com
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - jira multiple users: 
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      ----- 
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - name: mdavis-sd-demo
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - key: mdavis-sd-demo
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - display name: Mitch Davis
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - email address: mdavis-sd-demo@example.com
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      ----- 
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - name: assetuser
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - key: assetuser
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - display name: Asset User 1
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - email address: asset-user@example.com
2020-06-23 17:37:28,858 DEBUG [groovy.script]:      - active: true
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - listbox: lahmacun
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - listbox multiple: 
2020-06-23 17:37:28,858 DEBUG [groovy.script]:        - mavi
2020-06-23 17:37:28,858 DEBUG [groovy.script]:        - kırmızı
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - number long: 120
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - number float: 150
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - radio: paranoid
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - text: sample text here
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - text area: more sample 
text here
2020-06-23 17:37:28,858 DEBUG [groovy.script]: all - url: https://jira-test.snapbytes.com/
2020-06-23 17:37:28,858 DEBUG [groovy.script]: --- System Fields ----------------------
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.quantity: 100
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.costCurrency: USD
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.reservedFor: tyler
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.assetTag: asset tag
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.owner: agrant-sd-demo
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.serialNumber: my serial 101
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.status: 1 (Display value: In stock)
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.installDate: 2020-06-10
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.managedBy: mdavis-sd-demo
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.substatus: 2 (Display value: Reserved)
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.assignee: JIRAUSER10303
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.belongsToGroup: [ "jira-servicedesk-users", "jira-software-users" ]
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.location: 2
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.cost: 1200
2020-06-23 17:37:28,859 DEBUG [groovy.script]: asset.invoiceNumber: my invoice 10387

  • No labels