Name

global.AssetandCI

Description

Synchronization code between asset and CI records

Script

var AssetandCI = Class.create();
AssetandCI.prototype = {
  initialize : function() {
  },

  createMultipleAssets : function(categoryId, modelId) {
  	var category= new GlideRecord('cmdb_model_category');
  	category.query("sys_id", categoryId);
      if (!category.next() || category.asset_class.nil() || category.enforce_verification || category.cmdb_ci_class == '')
      return;

      var ci = new GlideRecord(category.cmdb_ci_class);
      if (!ci.isValid()) return;
  	ci.addQuery('asset', '').addOrCondition('asset', null);
  	
  	if (modelId) { ci.addQuery('model_id', modelId); }
  	ci.addQuery('model_id.cmdb_model_category', 'CONTAINS', category.sys_id);

  	// Considering only CIs of same class.
  	if(ci.isValidField('sys_class_name')) {
  		ci.addQuery('sys_class_name', category.cmdb_ci_class);
  	}

  	ci.query();

  	while (ci.next()) {
  		if (ci.model_id != null && !ci.model_id.nil() && (ci.model_id.asset_tracking_strategy == 'do_not_track' || ci.model_id.asset_tracking_strategy == 'track_as_consumable'))
  			continue;
  		var asset = new GlideRecord(category.asset_class);
  		asset.initialize();
  		asset.ci = ci.sys_id;
  		asset.model_category = category.sys_id;
  		// inherit values from CI for shared fields
  		var sync = new AssetAndCISynchronizer();
  		sync.syncRecordsWithoutUpdate(ci, asset, 'alm_asset', true);
  		// insert assert record and stick its reference in the CI
  		ci.asset = asset.insert();
  		ci.update();
  	}
  },

  updateEmptyModels : function(modelCategory, ciClass) {
  	var ci = new GlideRecord('cmdb_ci');
  	ci.addQuery('sys_class_name', ciClass);
  	ci.addQuery('model_id', '').addOrCondition('model_id', null);
  	ci.query();

  	if (!ci.hasNext())
  		return;

  	var model = this._getUnknownModel(modelCategory);
  	while (ci.next()) {
  		ci.model_id = model;
  		ci.update();
  	}
  },

  deleteAsset : function(ci) {
  	var asset = new GlideRecord('alm_asset');
  	if (asset.get(ci.asset.toString()))
  		asset.deleteRecord();
  },

  deleteAssetOnly: function(asset) {
  	var ci = new GlideRecord('cmdb_ci');
  	if (ci.get(asset.ci)) {
  		ci.asset = '';
  		if (ci.update()) {
  			asset.ci = '';
  			if (asset.update())
  				asset.deleteRecord();
  		}
  	}
  },

  deleteCI : function(asset) {
  	var ci = new GlideRecord('cmdb_ci');
  	if (ci.get(asset.ci.toString()))
  		ci.deleteRecord();
  },

  /*
  * Create an asset associated bi-directionally to the ci if category allows.
  * Fields shared between the 2 tables are inherited from the source record
  * by the destination record. The asset is saved to DB but the CI needs to
  * be updated by caller code in order to prevent superfluous updates
  */
  createAsset : function(ci) {
  	this.createAssetByPass(ci, false, true);
  },

  /*
  * Create an asset associated bi-directionally to the ci if category allows.
  * Fields shared between the 2 tables are inherited from the source record
  * by the destination record. The asset is saved to DB but the CI needs to
  * be updated by caller code in order to prevent superfluous updates
  */
  createAssetByPass : function(ci, bypass, validateCIVerification) {
  	// DEF0403385 - If validateCIVerification is not passed then it is calculated based on bypass value
  	if (typeof validateCIVerification == 'undefined') {
  		validateCIVerification = !bypass;
  		
  	}
  	var result = {};
  	// Do we already have an asset?
  	if (!ci.asset.nil()) {
  		gs.log("Duplicate asset generation prevented for CI " + ci.name);
  		result.status = AssetandCI.RESULT_STATUS.ASSET_SKIPPED;
  		return result;
  	}

  	var category = this._getModelCategoryAndRepairModelAssociation(ci);
  	if (gs.nil(category)) {
  		result.status = AssetandCI.RESULT_STATUS.ASSET_SKIPPED;
  		return result;
  	}

  	// Check the model asset creation strategy
  	if (ci.model_id != null && !ci.model_id.nil() && (ci.model_id.asset_tracking_strategy == 'do_not_track' || ci.model_id.asset_tracking_strategy == 'track_as_consumable')) {
  		result.status = AssetandCI.RESULT_STATUS.ASSET_SKIPPED;
  		return result;
  	}

  	// Is there already an asset for this CI?
  	var otherAsset = new GlideRecord("alm_asset");
  	if (otherAsset.get('ci', ci.getValue('sys_id'))) {
  		// Is there already an CI for this asset?
  		var otherCI = new GlideRecord("cmdb_ci");
  		if (otherCI.get('asset', otherAsset.getValue('sys_id'))) {
  			result.status = AssetandCI.RESULT_STATUS.CI_ERROR;
  			result.comments = gs.getMessage('Record not match error - Asset({0}) record is synced to other CI({1}) record. Unable to sync multiple CIs to an asset.', [otherAsset.getValue('sys_id'), otherCI.getValue('sys_id')]);
  			return result;
  		} else {
  			ci.asset = otherAsset.sys_id;
  			gs.log("Duplicate asset generation (Existing asset) prevented for CI " + ci.name);
  			result.status = AssetandCI.RESULT_STATUS.ASSET_SKIPPED;
  			return result;
  		}
  	}

  	if (category != null && !category.nil() && !category.asset_class.nil() && (category.enforce_verification != true || (bypass == true && validateCIVerification == false && ci.unverified == true))) {
  		var asset = new GlideRecord(category.asset_class);
  		asset.initialize();
  		asset.ci = ci.sys_id;
  		asset.model_category = category.sys_id;
  		// inherit values from CI for shared fields
  		var sync = new AssetAndCISynchronizer();
  		sync.syncRecordsWithoutUpdate(ci, asset, 'alm_asset', bypass);
  		// insert assert record and stick its reference in the CI
  		ci.unverified = false;
  		var assetId = asset.insert();
  		if (assetId) {
  			ci.asset = assetId;
  			result.status = AssetandCI.RESULT_STATUS.ASSET_CREATED;
  			return result;
  		} else {
  			result.status = AssetandCI.RESULT_STATUS.CI_ERROR;
  			result.comments = gs.getMessage('Asset creation failed');
  			return result;
  		}
  	} else if (category != null && !category.nil() && !category.asset_class.nil() && category.enforce_verification == true) {
  		ci.unverified = true;
  		result.status = AssetandCI.RESULT_STATUS.CI_NEEDS_VERIFICATION;
  		return result;
  	} else {
  		result.status = AssetandCI.RESULT_STATUS.ASSET_SKIPPED;
  		return result;
  	}
  },

  /*
  * Create a CI associated bi-directionally to the asset if category and
  * model allow. Fields shared between the 2 tables are inherited from the
  * source record by the destination record. The CI is saved to DB but the
  * asset needs to be updated by caller code in order to prevent superfluous
  * updates
  */
  createCI : function(asset) {
  	// retrieve CI class from the model category if model doesn't override
  	// it as
  	// consumable or no tracking

  	// Skip if we already have a CI
  	if (!asset.ci.nil()) {
  		gs.log("Duplicate CI generation prevented for asset " + asset.display_name);
  		return;
  	}

  	// Is there a CI out there with this asset?
  	var otherCI = new GlideRecord("cmdb_ci");
  	otherCI.addQuery("asset", asset.sys_id);
  	otherCI.setLimit(1);
  	otherCI.query();
  	if (otherCI.next()) {
  		asset.ci = otherCI.sys_id;
  		gs.log("Duplicate CI generation (existing CI) prevented for asset " + asset.display_name);
  		return;
  	}

  	var ciClass = '';
  	if ('leave_to_category' == asset.model.asset_tracking_strategy
  		.toString())
  	ciClass = asset.model_category.cmdb_ci_class.toString();

  	if (ciClass != '') {
  		var ciSysId;
  		var ci = new GlideRecord(ciClass);
  		ci.initialize();
  		ci.asset = asset.sys_id;
  		// in the absence of a calculated name for CIs, set
  		// something so links don't appear blank
  		ci.name = asset.model.name;
  		// Populate manufacturer
  		ci.manufacturer = asset.model.manufacturer;
  		// inherit values from asset for shared fields
  		var sync = new AssetAndCISynchronizer();
  		sync.syncRecordsWithoutUpdate(asset, ci, 'cmdb_ci', false);
  		// insert CI record and stick its reference in the asset
  		var assetCMDBUtil = new AssetCMDBUtil();
  		if (asset.install_status != 2
  			&& !gs.nil(asset.serial_number)
  			&& assetCMDBUtil.isSerialNumberMandatory(asset.model_category)) {
  			ci.name = asset.serial_number + ' - ' + asset.model.name;
  			ciSysId = assetCMDBUtil.createCIUsingIRE(ci);
  		} else {
  			ciSysId = ci.insert();
  		}
  		asset.ci = ciSysId;
  	}
  },

  cascadeAssetCreationFromBundle : function(asset) {
  	this._createAssetComponentsAndAlterAssetAsNeeded(asset);
  },

  /*
  * Reference qualifier to pick parent
  */
  assetParentRefQual : function(asset) {
  	var refQual = '';
  	if (asset.substatus != 'pre_allocated') {
  		// only pre-allocated assets can be children of another
  		// pre-allocated asset
  		refQual = 'substatus=^ORsubstatus!=pre_allocated^';
  	}
  	// in any case, stacks, consumables and licenses cannot be parents
  	refQual += 'quantity=1^sys_class_name!=alm_consumable^sys_class_name!=alm_license';
  	return refQual;
  },

  /*
  * Merge a single CI into another one and delete the source CI
  */
  mergeCI : function(ci, new_ci) {
  	var oci = new GlideRecord("cmdb_ci");
  	oci.get(ci);

  	var nci = new GlideRecord("cmdb_ci");
  	nci.get(new_ci);

  	nci.serial_number = oci.serial_number;
  	nci.discovery_source = oci.discovery_source;
  	nci.mac_address = oci.mac_address;
  	nci.name = oci.name;
  	nci.operational_status = oci.operational_status;
  	nci.correlation_id = oci.correlation_id;
  	nci.model_id = oci.model_id;
  	nci.update();

  	if (!nci.asset.nil()) {
  		var asset = new GlideRecord("alm_asset");
  		asset.get(nci.asset);
  		asset.model = oci.model_id;
  		asset.update();
  	}

  	oci.deleteRecord();
  },

  /*
  * trickle information from parent (grParentAsset) to child (grAsset) does
  * not trigger update overrides child information if any, even when parent
  * field is null
  */
  inheritInfoFromParentAsset : function(grAsset, grParentAsset) {
  	var gr = new GlideRecord(grParentAsset.getTableName());
  	if (AssetUtils.IS_ITAM_COMMON_ACTIVE && sn_itam_common.ITAMCommonPalletsUtil &&
  		gr.instanceOf(sn_itam_common.ITAMCommonPalletsUtil.PALLET_BASE_TABLE)) {
  		// inherit information from pallet
  		sn_itam_common.ITAMCommonPalletsUtil.syncInformationFromPallet(grAsset, grParentAsset);
  	} else {
  		this._inheritInfoFromParentAsset(grAsset, grParentAsset, grAsset.sys_class_name);
  	}
  },

  /*
  * trickle information from parent (grParentAsset) to child (grAsset) does
  * not trigger update overrides child information if any, even when parent
  * field is null
  */
  _inheritInfoFromParentAsset : function(grAsset, grParentAsset, childClass) {
  	var isChildConsumable = (childClass == 'alm_consumable');
  	var isChildPreAllocated = (grAsset.substatus == 'pre_allocated');
  	// consumable state is always controlled by the consumable itself
  	// pre-allocated is 'in transit' (9) when parent is too, 'in stock' (6)
  	// otherwise
  	// parent governs state of all real, allocated assets
  	if (!isChildConsumable) {
  		if (isChildPreAllocated) {
  			if (grParentAsset.install_status == '9')
  				grAsset.install_status = '9';
  			else
  				grAsset.install_status = '6';
  		} else
  		grAsset.install_status = grParentAsset.install_status;
  	}

  	// substate trickle only for real, allocated assets
  	if (!isChildConsumable && !isChildPreAllocated)
  		grAsset.substatus = grParentAsset.substatus;

  	// other fields whose values need to always trickle down
  	grAsset.location = grParentAsset.location;
  	grAsset.stockroom = grParentAsset.stockroom;
  	grAsset.company = grParentAsset.company;
  	grAsset.department = grParentAsset.department;
  	
  	// Commented the below if condition as PM confirmed, child consumable will always be in sync with parent
  	//if(!isChildConsumable || grAsset.assigned_to == '')
  	grAsset.assigned_to = grParentAsset.assigned_to;
  	
  	// DEF0130158: Updating reserved_for of asset with parent reserved_for
  	grAsset.reserved_for = grParentAsset.reserved_for;
  	grAsset.managed_by = grParentAsset.managed_by;
  	grAsset.owned_by = grParentAsset.owned_by;
  	grAsset.assigned = grParentAsset.assigned;
  	grAsset.cost_cc = grParentAsset.cost_cc;
  	grAsset.cost_center = grParentAsset.cost_center;
  	if (grAsset.isValidField('sn_itam_common_aisle')) {
  		grAsset.sn_itam_common_aisle = grParentAsset.sn_itam_common_aisle;
  	}
  	if (grAsset.isValidField('sn_itam_common_space')) {
  		grAsset.sn_itam_common_space = grParentAsset.sn_itam_common_space;
  	}
  },

  /*
  * Retrieve asset creation strategy based on model and class
  * Also update empty model to Unknown model if category calls
  * for asset tracking
  * (legacy: allow empty models for items that don't pertain to
  * asset management)
  */
  _getModelCategoryAndRepairModelAssociation : function(ci) {
  	var modelCategory = new GlideRecord('cmdb_model_category');

  	// category has to match ci class
  	modelCategory.addQuery('cmdb_ci_class', ci.sys_class_name);

  	// category has to match model expectations when model is defined
  	var modelCategoryList = '';
  	if (!ci.model_id.nil())
  		modelCategoryList = ci.model_id.cmdb_model_category.toString();

  	modelCategory.query();
  	if (modelCategory.next()) {
  		if (modelCategoryList === '') {
  			if (ci.model_id.nil()) {
  				// when no model was set, we want to allow creation nonetheless and
  				// update the ci to refer to the Unknown model of the retrieved category
  				ci.model_id = this._getUnknownModel(modelCategory.sys_id);
  			} else {
  				// when no category is specified in the model, we want to allow creation nonetheless and
  				// update the model to support this class
  				var model = new GlideRecord('cmdb_model');
  				if (model.get(ci.model_id)) {
  					model.cmdb_model_category = modelCategory.sys_id;
  					model.update();
  				} else {
  					ci.model_id = this._getUnknownModel(modelCategory.sys_id);
  				}
  			}
  		}
  		return modelCategory;
  	}
  	return null;
  },

  /*
  * - Change asset definition and class to main component's definition and
  * class for abstract bundles - Cycle through bundle definition and
  * instantiate all components - Recursion is naturally handled by the same
  * logic being called for each component
  */
  _createAssetComponentsAndAlterAssetAsNeeded : function(asset) {
  	// gather model bundle information
  	var components = this._getBundleDefinition(asset.model);
  	var masterIndex = components.pop();
  	var models = this._getModelsDefinitions(components);
  	var modelCategories = this._getCategoriesDefinitions(components);
  	var attachToAsset = false;

  	if (asset.model_category.bundle.toString() == 'true') {
  		// abstract bundle
  		var model = "";
  		var category = "";
  		var assetClass = "";
  		if (masterIndex > -1) {
  			// there is a master component : redirect current asset to it
  			var mainComponent = components[masterIndex];
  			model = models[mainComponent.model];
  			category = modelCategories[mainComponent.modelCategory];
  			assetClass = this._getAssetClass(model, category);
  			asset.sys_class_name = assetClass;
  			asset.model = mainComponent.model;
  			asset.model_category = mainComponent.modelCategory;
  			asset.quantity = 1;
  			// we know main components can't be consumables
  			components.splice(masterIndex, 1);
  			// and attach all future assets to it
  			attachToAsset = true;
  		} else {
  			// items are bundled but not attached together, redirect current
  			// asset to first component
  			var firstComponent = components.shift();
  			model = models[firstComponent.model];
  			category = modelCategories[firstComponent.modelCategory];
  			assetClass = this._getAssetClass(model, category);
  			asset.sys_class_name = assetClass;
  			asset.model = firstComponent.model;
  			asset.model_category = firstComponent.modelCategory;
  			asset.quantity = 1;
  			// set consumable to Consumed
  			if (assetClass == 'alm_consumable')
  				asset.install_status = 10;
  			// and don't attach future assets to it
  			attachToAsset = false;
  		}
  	} else {
  		// concrete bundle : asset stays as is and we attach all future
  		// assets to it
  		attachToAsset = true;
  	}

  	// asset relationship
  	var parentId = '';
  	if (attachToAsset == true)
  		parentId = asset.sys_id.toString();
  	else
  		parentId = asset.parent.toString();

  	// create and attach components
  	var newAssetClass, component;
  	for (var i = 0; i < components.length; i++) {
  		component = components[i];
  		// model and category of the component control the existence of
  		// asset record and its class
  		newAssetClass = this._getAssetClass(models[component.model],
  		modelCategories[component.modelCategory]);
  		if (newAssetClass !== '') {
  			var newAsset = new GlideRecord(newAssetClass);
  			newAsset.model = component.model;
  			newAsset.model_category = component.modelCategory;
  			newAsset.parent = parentId;
  			// set consumable to Consumed
  			if (newAssetClass == 'alm_consumable')
  				newAsset.install_status = 10;
  			// propagate state and assignment fields
  			this._inheritInfoFromParentAsset(newAsset, asset, newAssetClass);
  			// save structure
  			newAsset.insert();
  		}
  	}
  },

  /*
  * Helper that determines what kind of asset (if any) a model - category
  * pair would instantiate
  */
  _getAssetClass : function(model, category) {
  	var assetClass = '';
  	if (model.assetStrategy == 'track_as_consumable')
  		assetClass = 'alm_consumable';
  	else if (model.assetStrategy == 'do_not_track')
  		assetClass = '';
  	else if (model.assetStrategy == 'leave_to_category') {
  		if (category.bundle != 'true')
  			assetClass = category.assetClass;
  		else
  			assetClass = 'alm_asset';
  	}
  	return assetClass;
  },

  /*
  * Retrieves and stores the complete definition of a bundle
  */
  _getBundleDefinition : function(modelId) {
  	var components = [];
  	var masterIndex = -1;
  	var grComponent = new GlideRecord('cmdb_m2m_model_component');
  	grComponent.addQuery('parent', modelId);
  	grComponent.query();
  	while (grComponent.next()) {
  		// retrieve component definition
  		var component = {};
  		component.model = grComponent.child.toString();
  		component.modelCategory = grComponent.model_category.toString();
  		// store component definition
  		components.push(component);
  		// mark the component to attach others to when appropriate
  		if (grComponent.master.toString() == 'true') {
  			if (masterIndex > -1) {
  				// error situation
  				gs.print('Bundle with model ID ' + modelId
  				+ ' has more than one main component defined');
  			}
  			masterIndex = components.length - 1;
  		}
  	}
  	// stick master index at the end of components list to retrieve master
  	// component faster
  	components.push(masterIndex);
  	return components;
  },

  /*
  * Helper that stores model information for all components of a bundle (no
  * recursion) to limit number of queries
  */
  _getModelsDefinitions : function(components) {
  	var ids = [];
  	for ( var i = 0; i < components.length; i++)
  		ids.push(components[i].model);
  	var models = {};
  	var grModel = new GlideRecord('cmdb_model');
  	grModel.addQuery('sys_id', 'IN', ids.toString());
  	grModel.query();
  	while (grModel.next()) {
  		var id = grModel.sys_id.toString();
  		models[id] = {
  			'assetStrategy' : grModel.asset_tracking_strategy.toString(),
  			'bundle' : grModel.bundle.toString()
  		};
  	}
  	return models;
  },

  /*
  * Helper that stores category information for all components of a bundle
  * (no recursion) to limit number of queries
  */
  _getCategoriesDefinitions : function(components) {
  	var ids = [];
  	for ( var i = 0; i < components.length; i++)
  		ids.push(components[i].modelCategory);
  	var categories = {};
  	var grCategory = new GlideRecord('cmdb_model_category');
  	grCategory.addQuery('sys_id', 'IN', ids.toString());
  	grCategory.query();
  	while (grCategory.next()) {
  		var id = grCategory.sys_id.toString();
  		categories[id] = {
  			'assetClass' : grCategory.asset_class.toString(),
  			'ciClass' : grCategory.cmdb_ci_class.toString(),
  			'bundle' : grCategory.bundle.toString()
  		};
  	}
  	return categories;
  },

  /*
  * Return a model named 'Unknown' associated with the given category
  * Create and return one such model if none exists yet
  */
  _getUnknownModel: function(modelCategory) {
  	var model = new GlideRecord('cmdb_model');
  	model.addQuery('name','Unknown');
  	model.addQuery('cmdb_model_category','CONTAINS', modelCategory);
  	model.query();
  	if (model.next())
  		return model.sys_id;

  	model = new GlideRecord('cmdb_model');
  	model.initialize();
  	model.name = 'Unknown';
  	model.cmdb_model_category = modelCategory;
  	return model.insert();
  },

  _createMultipleAssetQueueRecords: function(ciGr) {
  	var ciInfo = {};
  	var count = 0;
  	var C_CHUNK_SIZE = 500;
  	while (ciGr.next()) {
  		ciInfo[ciGr.getUniqueValue()] = ciGr.getValue('sys_class_name');
  		count ++;
  		if (count >= C_CHUNK_SIZE) {
  			this._createBatchAssetQueueRecords(ciInfo);
  			// Reset variables
  			count = 0;
  			ciInfo = {};
  		}
  	}
  	if (count > 0) {
  		this._createBatchAssetQueueRecords(ciInfo);
  	}
  },
  _createBatchAssetQueueRecords: function(ciInfo) {
  	var ciId;
  	var ciIds = Object.keys(ciInfo);
  	if (ciIds.length > 0) {
  		// Avoid duplicate CI references in queue.
  		var assetQueueGr = new GlideRecord(AssetandCI.ASSET_CREATION_QUEUE.TABLE);
  		assetQueueGr.addQuey(AssetandCI.ASSET_CREATION_QUEUE.DOCUMENT_ID_COLUMN, 'IN', ciIds);
  		assetQueueGr.query();
  		while (assetQueueGr.next()) {
  			delete ciInfo[assetQueueGr.getValue(AssetandCI.ASSET_CREATION_QUEUE.DOCUMENT_ID_COLUMN)];
  		}
  		// Create queue record with CI reference.
  		for (ciId in ciInfo) {
  			AssetandCI.createAssetQueueRecord(ciId, ciInfo[ciId]);
  		}
  	}
  },
  createAssetQueueForModelCategory: function(modelCategory) {
  	var modelCategoryGr = new GlideRecord('cmdb_model_category');
  	if (modelCategoryGr.get(modelCategory) && !modelCategoryGr.asset_class.nil() && !modelCategoryGr.cmdb_ci_class.nil()) {
  		var ciClass = modelCategoryGr.getValue('cmdb_ci_class');
  		var ciGr = new GlideRecord(ciClass);
  		ciGr.addQuery('sys_class_name', ciClass);
  		ciGr.addNullQuery('asset');
  		ciGr.addNullQuery('model_id').addOrCondition('model_id.asset_tracking_strategy', 'leave_to_category');
  		ciGr.query();
  		this._createMultipleAssetQueueRecords(ciGr);
  	}
  },
  createAssetQueueForModel: function(model, modelCategory) {
  	var modelCategoryGr = new GlideRecord('cmdb_model_category');
  	if (modelCategoryGr.get(modelCategory) && !modelCategoryGr.asset_class.nil() && !modelCategoryGr.cmdb_ci_class.nil()) {
  		var ciClass = modelCategoryGr.getValue('cmdb_ci_class');
  		var ciGr = new GlideRecord(ciClass);
  		ciGr.addQuery('sys_class_name', ciClass);
  		ciGr.addNullQuery('asset');
  		ciGr.addQuery('model_id', model);
  		ciGr.addQuery('model_id.asset_tracking_strategy', 'leave_to_category');
  		ciGr.query();
  		this._createMultipleAssetQueueRecords(ciGr);
  	}
  },

  type : 'AssetandCI'
};
AssetandCI.ASSET_ASYNC_PROPERTY = 'glide.create_alm_asset.async';
AssetandCI.ASSET_CREATION_QUEUE = {
  TABLE: 'alm_asset_creation_queue',
  DOCUMENT_ID_COLUMN: 'source_id',
  TABLE_COLUMN: 'table',
  STATE_COLUMN: 'state',
  COMMENTS_COLUMN: 'comments'
};
AssetandCI.ASSET_PROPERTY = 'asset_property';
AssetandCI.ASYNC_JOB_TIMEOUT_PROPERTY = 'com.snc.asset_management.assetAsyncJob.timeoutPeriod';
AssetandCI.RESULT_STATUS = {
  ASSET_CREATED: 'ASSET_CREATED',
  ASSET_SKIPPED: 'ASSET_SKIPPED',
  CI_NEEDS_VERIFICATION: 'CI_NEEDS_VERIFICATION',
  CI_DELETED: 'CI_DELETED',
  CI_ERROR: 'CI_ERROR',
  NOT_SUPPORTED: 'NOT_SUPPORTED'
};
AssetandCI.modelCategoryCIClassMap = {};
AssetandCI.isAssetRequiredForCI = function(ci) {
  var ciClass = ci.getValue('sys_class_name');
  if (!(ciClass in AssetandCI.modelCategoryCIClassMap)) {
  	// Reset modelCategoryCIClassMap to avoid memory issues
  	if (Object.keys(AssetandCI.modelCategoryCIClassMap).length === 10000) {
  		AssetandCI.modelCategoryCIClassMap = {};
  	}
  	var modelCategory = new GlideRecord('cmdb_model_category');
  	if (modelCategory.get('cmdb_ci_class', ciClass)) {
  		if (!modelCategory.asset_class.nil() && !modelCategory.cmdb_ci_class.nil()) {
  			AssetandCI.modelCategoryCIClassMap[ciClass] = true;
  		}
  	}
  	if (!(ciClass in AssetandCI.modelCategoryCIClassMap)) {
  		AssetandCI.modelCategoryCIClassMap[ciClass] = false;
  	}
  }
  if (!AssetandCI.modelCategoryCIClassMap[ciClass]) {
  	return AssetandCI.modelCategoryCIClassMap[ciClass];
  } else {
  	if (ci.model_id.nil() || (ci.model_id.asset_tracking_strategy != 'do_not_track' && ci.model_id.asset_tracking_strategy != 'track_as_consumable')) {
  		return true;
  	}
  	return false;
  }
};
AssetandCI.createAssetQueueRecord = function(ciID, ciTable) {
  var assetBacklogGr = new GlideRecord(AssetandCI.ASSET_CREATION_QUEUE.TABLE);
  assetBacklogGr.initialize();
  assetBacklogGr.setValue(AssetandCI.ASSET_CREATION_QUEUE.DOCUMENT_ID_COLUMN, ciID);
  assetBacklogGr.setValue(AssetandCI.ASSET_CREATION_QUEUE.TABLE_COLUMN, ciTable);
  assetBacklogGr.insert();
};
AssetandCI.canCreateCIForAsset = function (current) {
  return (
  	current.sys_class_name != 'alm_consumable'
  	&& current.sys_class_name != 'alm_license'
  	&& current.ci.nil()
  	&& current.substatus != 'pre_allocated'
  	&& current.model_category.cmdb_ci_class != ''
  );
};
AssetandCI.shouldBundleEnterpriseAssets = function(asset) {
  // Check if asset is an enterprise asset
  if (AssetandCI.IS_EAM_ACTIVE && sn_ent.EnterpriseContentEAMUtils.isEnterpriseClassAsset 
  	&& sn_ent.EnterpriseContentEAMUtils.isEnterpriseClassAsset(asset.sys_class_name)) {
      if (sn_eam.EAMUtils.cascadeENTAssetCreationFromBundle || asset.model.ref_sn_ent_model.model_type == 'user_assembled') {
  		// If EAM specific BR is present or asset is user-assembled
  		// Do not execute the base BR
          return false;
      }
  }
  // Either EAM or ENT not installed or not an enterprise asset
  return true;
};
AssetandCI.IS_EAM_ACTIVE = GlidePluginManager.isActive('com.sn_eam') || GlidePluginManager.isActive('sn_eam');

Sys ID

19c0f3603703100044e0bfc8bcbe5d9b

Offical Documentation

Official Docs: