How do I edit the AssetAndCISynchronizer to include a newly created field?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-25-2016 03:22 PM
I created a new field in both the CMDB and the Asset tables called u_firmware_version. I would like to add it to the AssetAndCISynchronizer script include so that it updates just like all the other fields do. Our AssetAndCISynchronizer script is below. I tried just adding "changes += this._syncField('u_firmware_version', source, destination);" under the tag " // synchronize data for fields that have the same name in both tables" but that didn't work. How do I do this? Thanks!
var AssetAndCISynchronizer = Class.create();
AssetAndCISynchronizer.prototype = {
initialize : function() {
},
/*
* Synchronize data between related asset and CI records Record linked to
* source record in the destination table will get updated
*/
syncRecords : function(source, destinationBaseTable) {
var destination = new GlideRecord(destinationBaseTable);
var foreignKey = (destinationBaseTable == 'alm_asset') ? 'asset' : 'ci';
var destinationId = source[foreignKey].toString();
destination = null;
if ('cmdb_ci' == destinationBaseTable) {
// when synchronizing to a CI, we have to check first for hardware
// because
// some status fields exist only on hardware table
destination = new GlideRecord('cmdb_ci_hardware');
// fast forward glide record to proper destination ci,
// reset to null if no hardware ci found and proceed with base ci
destination.query("sys_id", destinationId);
if (!destination.next())
destination = null;
}
if (destination == null) {
destination = new GlideRecord(destinationBaseTable);
// fast forward glide record to proper destination record
// abandon processing if no such record found
destination.query("sys_id", destinationId);
if (!destination.next())
return;
}
var changes = this.syncRecordsWithoutUpdate(source, destination,
destinationBaseTable, false);
if (changes > 0) {
destination.skip_sync = true;
destination.update();
}
},
/*
* Update the fields of destination record with information from source
* record but do not trigger an update of the destination record itself
* if asynchUpdate, destination will be modified even if source hasn't changed
*/
syncRecordsWithoutUpdate : function(source, destination,
destinationBaseTable, asyncUpdate) {
var changes = 0;
// Sanity protection
var me = source.getValue('sys_id') + '';
if ('cmdb_ci' == destinationBaseTable) {
// Check that we are pointing at ourselves
var back = destination.getValue('asset') + '';
if ( back != me) {
gs.log('Aborting Asset to CI sync due to pointer mismatch');
return 0;
}
}
if ('alm_asset' == destinationBaseTable) {
// Check that we are pointing back at ourselves
var back = destination.getValue('ci') + '';
if ( back != me) {
gs.log('Aborting CI to Asset sync due to pointer mismatch');
return 0;
}
}
// synchronize data for fields that have the same name in both tables
changes += this._syncField('location', source, destination);
changes += this._syncField('asset_tag', source, destination);
changes += this._syncField('serial_number', source, destination);
changes += this._syncField('assigned_to', source, destination);
changes += this._syncField('assigned', source, destination);
changes += this._syncField('checked_in', source, destination);
changes += this._syncField('checked_out', source, destination);
changes += this._syncField('company', source, destination);
changes += this._syncField('cost_center', source, destination);
changes += this._syncField('delivery_date', source, destination);
changes += this._syncField('department', source, destination);
changes += this._syncField('due_in', source, destination);
changes += this._syncField('due', source, destination);
changes += this._syncField('gl_account', source, destination);
changes += this._syncField('invoice_number', source, destination);
changes += this._syncField('justification', source, destination);
changes += this._syncField('lease_id', source, destination);
changes += this._syncField('managed_by', source, destination);
changes += this._syncField('order_date', source, destination);
changes += this._syncField('owned_by', source, destination);
changes += this._syncField('support_group', source, destination);
changes += this._syncField('supported_by', source, destination);
changes += this._syncField('vendor', source, destination);
changes += this._syncField('warranty_expiration', source, destination);
changes += this._syncField('po_number', source, destination);
changes += this._syncField('purchase_date', source, destination);
changes += this._syncField('install_date', source, destination);
changes += this._syncField('sys_domain', source, destination);
// fields that differ slightly
changes += this._syncModel(destinationBaseTable, source, destination);
changes += this._syncCost(destinationBaseTable, source, destination, asyncUpdate);
// fields that differ wildly
changes += this._syncState(destinationBaseTable, source, destination, asyncUpdate);
return changes;
},
/*
* Simple synchronization: fields have the same name in both tables and
* value simply need to be carried over
*/
_syncField : function(fieldName, source, destination) {
if (source.getElement(fieldName) != destination.getElement(fieldName)) {
if (!source.getElement(fieldName).hasValue())
destination.setValue(fieldName, '');
else
destination.setValue(fieldName, source.getValue(fieldName));
return 1;
}
return 0;
},
/*
* Model synchronization: model fields are named differently but should hold
* the same value
*/
_syncModel : function(destinationBaseTable, source, destination) {
if ('alm_asset' == destinationBaseTable) {
if (source.model_id != destination.model) {
destination.model = source.model_id;
return 1;
}
return 0;
}
if ('cmdb_ci' == destinationBaseTable) {
if (source.model != destination.model_id) {
destination.model_id = source.model;
return 1;
}
return 0;
}
return 0;
},
/*
* Cost synchronization: cost fields are named differently and have
* different data format
*/
_syncCost : function(destinationBaseTable, source, destination, asyncUpdate) {
if ('alm_asset' == destinationBaseTable) {
if (source.cost.changes() || source.cost_cc.changes() || asyncUpdate) {
//cost fields are informational only on ci side and cannot be modified
return 0;
}
return 0;
}
if ('cmdb_ci' == destinationBaseTable) {
if (source.cost.changes() || asyncUpdate) {
var field = source.getElement('cost');
destination.cost = field.getCurrencyValue();
destination.cost_cc = field.getCurrencyCode();
return 2;
}
return 0;
}
return 0;
},
/*
* State fields are a different in name, number and data format. Careful
* mapping needs to be done.
*/
_syncState : function(destinationBaseTable, source, destination, asyncUpdate) {
if (destinationBaseTable == 'alm_asset')
return this._inferAssetStatuses(source, destination, asyncUpdate);
else if (destinationBaseTable == 'cmdb_ci')
return this._inferCIStatuses(source, destination, asyncUpdate);
return 0;
},
/*
* Infer asset state fields from ci state fields and update as appropriate
*/
_inferAssetStatuses : function(ci, asset, asyncUpdate) {
// HW statuses take precedence over CI statuses
if (this._isHardwareCI(ci)) {
if (!ci.hardware_status.changes() && !ci.hardware_substatus.changes() && !asyncUpdate)
return 0;
return this._inferAssetStatusesHardware(ci, asset);
} else {
if (!ci.install_status.changes() && !asyncUpdate)
return 0;
return this._inferAssetStatusesBase(ci, asset);
}
},
/*
* Infer asset ci fields from asset state fields and update as appropriate
*/
_inferCIStatuses : function(asset, ci, asyncUpdate) {
if (!asset.install_status.changes() && !asset.substatus.changes() && !asyncUpdate)
return 0;
var changes = this._inferCIStatusesBase(asset, ci);
if (this._isHardwareCI(ci))
changes += this._inferCIStatusesHardware(asset, ci);
return changes;
},
/*
* Look for clues that the ci is a hardware ci
*/
_isHardwareCI : function(ci) {
return (ci.hardware_status != undefined);
},
/*
* Figure out what CI install status should be based on asset's state and
* substate
*/
_inferCIStatusesBase : function(asset, ci) {
var status = asset.install_status.toString();
var substatus = asset.substatus.toString();
switch (status) {
case '6':
ci.install_status = this
._inferCIStatusForInStockOrInTransitAsset(substatus);
return 1;
case '8':
ci.install_status = this._inferCIStatusForMissingAsset(substatus);
return 1;
case '9':
ci.install_status = this
._inferCIStatusForInStockOrInTransitAsset(substatus);
return 1;
default:
ci.install_status = status;
return 1;
}
},
/*
* Figure out what hardware CI status and substatus should be based on
* asset's state and substate
*/
_inferCIStatusesHardware : function(asset, ci) {
var status = asset.install_status.toString();
var substatus = asset.substatus.toString();
switch (status) {
case '1':
ci.hardware_status = 'installed';
ci.hardware_substatus = 'in_use';
return 2;
case '2':
ci.hardware_status = 'on_order';
ci.hardware_substatus = '';
return 2;
case '3':
ci.hardware_status = 'in_maintenance';
ci.hardware_substatus = '';
return 2;
case '6':
ci.hardware_status = this
._inferHardwareCIStatusForInStockAsset(substatus);
ci.hardware_substatus = this
._inferHardwareCISubtatusForInStockAsset(substatus);
return 2;
case '7':
ci.hardware_status = 'retired';
ci.hardware_substatus = this
._inferHardwareCISubtatusForRetiredAsset(substatus);
return 2;
case '8':
if ('lost' == substatus) {
ci.hardware_status = 'retired';
ci.hardware_substatus = 'lost';
} else {
ci.hardware_status = 'stolen';
ci.hardware_substatus = '';
}
return 2;
case '9':
ci.hardware_status = 'in_transit';
ci.hardware_substatus = this
._inferHardwareCISubtatusForInTransitAsset(substatus);
return 2;
default:
return 0;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCIStatusForInStockAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'available':
case 'reserved':
return 'in_stock';
case 'pending_disposal':
return 'in_disposition';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForInStockAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'available':
case 'reserved':
return assetSubstatus;
default:
return '';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForRetiredAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'disposed':
return 'scrapped';
case 'vendor_credit':
return 'credit';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForInTransitAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'defective':
return 'repairable';
case 'pending_disposal':
return 'disposable';
case 'pending_install':
return 'reserved';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferCIStatusForInStockOrInTransitAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'pending_install':
return '4';
case 'pending_repair':
case 'defective':
return '5';
default:
return '6';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferCIStatusForMissingAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'stolen':
return '8';
default:
return '100'; // lost
}
},
/*
* Figure out what asset state and substate should be based on ci's install
* status
*/
_inferAssetStatusesBase : function(ci, asset) {
var status = '';
var substatus = '';
status = ci.install_status.toString();
switch (status) {
case '4':
asset.install_status = '6';
asset.substatus = 'pending_install';
return 2;
case '5':
asset.install_status = '6';
asset.substatus = 'pending_repair';
return 2;
case '6':
asset.install_status = '6';
asset.substatus = 'available';
return 2;
case '8':
asset.install_status = '8';
asset.substatus = 'stolen';
return 2;
case '100':
asset.install_status = '8';
asset.substatus = 'lost';
return 2;
default:
asset.install_status = ci.install_status;
asset.substatus = '';
return 2;
}
},
/*
* Figure out what asset state and substate should be based on hardware ci's
* state and substate
*/
_inferAssetStatusesHardware : function(ci, asset) {
var status = '';
var substatus = '';
var hwStatus = ci.hardware_status.toString();
var hwSubstatus = ci.hardware_substatus.toString();
switch (hwStatus) {
case 'installed':
asset.install_status = '1';
asset.substatus = '';
return 2;
case 'in_maintenance':
asset.install_status = '3';
asset.substatus = '';
return 2;
case 'in_stock':
asset.install_status = '6';
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'in_transit':
asset.install_status = '9';
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'defective':
asset.install_status = '6';
asset.substatus = 'defective';
return 2;
case 'in_disposition':
asset.install_status = '6';
asset.substatus = 'pending_disposal';
return 2;
case 'retired':
// duplicate error is irrelevant for asset -> no change
if ('duplicate_error' == hwSubstatus)
return 0;
// valid retirement reasons
asset.install_status = this
._inferAssetStatusFromHWSubstatusForRetiredCI(hwSubstatus);
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'on_order':
asset.install_status = '2';
asset.substatus = '';
return 2;
case 'pending_transfer':
asset.install_status = '6';
asset.substatus = 'pending_transfer';
return 2;
case 'stolen':
asset.install_status = '8';
asset.substatus = 'stolen';
return 2;
case 'pending_install':
asset.install_status = '6';
asset.substatus = 'pending_install';
return 2;
case 'pending_repair':
asset.install_status = '6';
asset.substatus = 'pending_repair';
return 2;
case 'out_of_stock':
// out of stock is irrelevant for asset -> no change
return 0;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferAssetStatusFromHWSubstatusForRetiredCI : function(hwSubstatus) {
switch (hwSubstatus) {
case 'lost':
case 'stolen':
return '8';
default:
return '7';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferAssetSubstatusFromHWStatusFields : function(hwStatus, hwSubstatus) {
switch (hwSubstatus) {
case 'in_use':
case 'received':
return 'available';
case 'repairable':
if ('in_transit' == hwStatus)
return 'defective';
return 'pending_repair';
case 'disposable':
return 'pending_disposal';
case 'credit':
return 'vendor_credit';
case 'scrapped':
return 'disposed';
case 'donated':
return 'donated';
case 'reserved':
case 'lost':
case 'sold':
case 'available':
case 'stolen':
return hwSubstatus;
default:
return '';
}
},
type : 'AssetAndCISynchronizer'
};

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-26-2016 10:58 AM
Stephanie,
Sorry to answer your question with another question, but.... well, here goes...:
- Why did you place this field on both the Asset and CI records?
- Firmware version is operational data that really belongs on a CI record
- If the goal is to make the information visible on the Asset record, too, you can do that without having a corresponding field on the Asset record
Creating the field in multiple locations adds complexity that usually is not required, as you are learning.
That being said, our Helsinki release adds the capability to easily map fields, but I still discourage storing the same data in multiple locations. It is better to determine the correct location to store it and create it there. From that point, you can make this information visible in other locations as you see fit, but it becomes significantly easier to maintain.
Ben
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-08-2016 04:16 AM
I could do with some help on this. We have added two user defined fields called network zone (u_network_zone) and hostname (u_hostname). These are stored on the CMDB_CI table.
We then reference them from the CI form and Asset form. They are only readable on the asset form.
However if we create a new asset whilst serial_number, u_physical location, install_status all work and copy across from one to another hostname and networkzone don't. In fact when we create the CI and enter them both, then the CI is created, the CI overwrites those fields with blank onto the asset form.
u_physical location whilst being another field defined by us this sits onthe alm_asset table and quite happily syncs between both. serial number sits onthe base configuration table (cmdb) and alm_asset.
PLease help as its causing major data issues for us

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-08-2016 06:36 AM
Damian,
I assume u_physical_location just displays on the CI form and is not synchronized.
As you have found, fields from a related record displayed on a form are read only. They also cannot be populated when creating a new record of that particular type. Because u_physical_location exists on the alm_asset table, you can populate it when you create a new asset record and it displays on the CI record. For u_network_zone and u_hostname, you need to create the asset first and then populate this information on the CI record.
If you are using import sets, you can create two transform maps to support this. The first would create the Asset record and the second would update the CI record that gets created. Just be sure to properly identify a field that uniquely identifies the record.
Ben
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-08-2016 06:50 AM
You're absolutely right physical location is not synced. Serial number is stored in two different places as is install_status, they are stored on cmdb base table and alm_asset tables. What I cannot get my head round is why they work and yet those other two don't.