Name

global.ProcurementUtils

Description

Utilities to handle PO/POL and asset creation

Script

var ProcurementUtils = Class.create();
ProcurementUtils.CONSUME_ASSET_TASK_TABLE = 'consume_asset_task';
ProcurementUtils.TRANSFER_ORDER_LINE_TABLE = 'alm_transfer_order_line';
ProcurementUtils.PURCHASE_ORDER_LINE_ITEM_TABLE = 'proc_po_item';
ProcurementUtils.ASSET_OPERATIONS = {
  CREATE: 'create',
  DELETE: 'delete'
};
ProcurementUtils.prototype = {
  METRIC_GROUP_MICROSOFT : "85253c9593222200caef14f1b47ffb02",
  LICENSE_METRIC_MS_PER_CORE : "ef64c370534323005d74ddeeff7b1238",
  LICENSE_METRIC_MS_PER_CORE_WITH_CAL : "22796ca493322200f2ef14f1b47ffb28",

  initialize: function() {
      this.failedAssets = [];
      this.isSAMSActive = GlidePluginManager.isActive('com.snc.sams');
      this.isHAMPActive = GlidePluginManager.isActive('com.sn_hamp');
      this.isEAMActive = GlidePluginManager.isActive('com.sn_eam');
      this.isITAMCommonActive = GlidePluginManager.isActive('com.sn_itam_common');
      this.procurementIntegrationActive = GlidePluginManager.isActive('com.sn_asset_proc_int');
  },

  createPO: function(request, vendor, destination) {
      var po = new GlideRecord('proc_po');
      po.due_by = request.due_date;
      po.init_request = request.sys_id;
      po.requested_by = request.opened_by;
      po.requested_for = request.requested_for;
      po.location = request.location;
      po.vendor = vendor;
      po.ship_to = destination;
      po.insert();
      return po;
  },

  createPOLine: function(po, item, qty, vendor, consolidate, destination, vendorPrice, listPrice, metricGroup, licenseMetric, partNumber, model) {
  if (po == '') {
          po = this._findPO(item, vendor, consolidate, destination);
      }
      var pol = new GlideRecord('proc_po_item');
      pol.purchase_order = po.sys_id;
      // Keeping the existing code as is and updating part number only for stock orders
      pol.part_number = item.cat_item.product_id;
      if (gs.nil(pol.part_number)) {
          pol.part_number = partNumber;
      }
      //Consider model variable for Stock order item
      if(!item.cat_item.model.nil()){
          pol.model = item.cat_item.model;
      }
      else if(this.isHAMPActive
      && sn_hamp.StockOrderUtils && sn_hamp.StockOrderUtils.isStockOrderItem
      && sn_hamp.StockOrderUtils.isStockOrderItem(item.cat_item.sys_id)) {
          pol.model = item.variables.model;
          pol.stock_order = true;
          pol.part_number = partNumber;
      }
      else if(this.isEAMActive
      && sn_eam.StockOrderUtils && sn_eam.StockOrderUtils.isStockOrderItem
      && sn_eam.StockOrderUtils.isStockOrderItem(item.cat_item.sys_id)) {
          pol.model = item.variables.model;
          pol.stock_order = true;
          pol.part_number = partNumber;
      }
      else {
          // model varibale passed in arguments will be set as POL model
          pol.model = model;
      }
      pol.product_catalog = item.cat_item;
      if (vendor == '') {
          pol.vendor = item.cat_item.vendor;
      }
      else {
          pol.vendor = vendor;
      }
      qty = (qty == '' || qty == 0)? item.quantity : qty;
      pol.ordered_quantity = qty;
      pol.received_quantity = 0;
      pol.remaining_quantity = qty;
      pol.cost = (vendorPrice == null || vendorPrice == undefined)? '' : vendorPrice;
      pol.list_price = (listPrice == null || listPrice == undefined || listPrice == 0)? pol.cost : listPrice;
      pol.short_description = item.cat_item.short_description;
      pol.request_line = item.sys_id;
      pol.requested_for = item.request.requested_for; //PRB633017 - requested_for should be the requested_for of the Request not the purchase order
      if (this.isSAMSActive && item.cat_item.model.sys_class_name == 'cmdb_software_product_model') {
          if (metricGroup && licenseMetric) {
              pol.metric_group = metricGroup;
              pol.license_metric = licenseMetric;

              //check if this is for Microsoft Per Core or Per Core(with CAL)
              if ((metricGroup === this.METRIC_GROUP_MICROSOFT) &&
                  (licenseMetric === this.LICENSE_METRIC_MS_PER_CORE || licenseMetric === this.LICENSE_METRIC_MS_PER_CORE_WITH_CAL)) {
                  pol.rights_per_license_pack = 1;
                  pol.number_of_packs = qty;
              }

          } else {
              gs.info('[ProcurementUtils] License Metric and Group not provided or invalid (' + metricGroup + ', ' + licenseMetric + ')');
          }
      }

      return pol.insert();
  },

  createAsset: function(po) {
      var pol = new GlideRecord('proc_po_item');
      pol.addQuery('purchase_order', po.sys_id);
      pol.query();
      while (pol.next()) {
          this.createPOLineAssets(pol);
      }
  },

  createPOLineAssets: function(pol) {
      if (pol.status == 'canceled' || pol.status == 'received' || pol.status == 'pending') {
          return;
      }
      var qty = pol.remaining_quantity;
      var type = (new AssetUtils()).getAssetOrConsumable(pol.model);
      if (type == 'consumable') {
          this._createConsumable(pol.purchase_order, pol, qty, 2);
      } else if (type == 'software') {
          for (var x = 0; x < qty; x++) {
              this._createSoftware(pol.purchase_order, pol, 2);
          }
      } else if (type == 'bundle' && this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils._createBundle) {
          for (var x = 0; x < qty; x++) {
              sn_hamp.HAMProcurementUtils._createBundle(pol.purchase_order, pol, 2);
          }
      }
  	else if (type == 'hardware' || type == 'asset') {
          for (var x = 0; x < qty; x++) {
              this._createAsset(pol.purchase_order, pol, 2);
          }
      }
  },

  hasHardwarePO: function(po){
      var pol = new GlideRecord('proc_po_item');
      pol.addQuery('purchase_order', po.sys_id);
      pol.query();
      while (pol.next()){
          if(this.hasHardwarePOLineItem(pol)) {
              return true;
          }
      }
      return false;
  },

  hasHardwarePOLineItem: function(pol, isStatusPendingAllowed){
  	var allowedStatuses = ['canceled', 'received'];
  	if(!isStatusPendingAllowed){
  		allowedStatuses.push('pending');
  	}
  	if (pol.status && allowedStatuses.indexOf(pol.status.toString()) == -1) {
          var qty = pol.remaining_quantity;
          var type = (new AssetUtils()).getAssetOrConsumable(pol.model);
          if (type == 'hardware' || type =='asset') {
              return true;
          }
      }
      return false;
  },

  hasHardwareAssetsForPO: function(po){
  	var pol = new GlideRecord('proc_po_item');
      pol.addQuery('purchase_order', po.sys_id);
      pol.query();
      while (pol.next()){
          if(this.hasHardwareAssetsForPOL(pol)) {
              return true;
          }
      }
      return false;
  },
  
  hasHardwareAssetsForPOL: function(pol){
  	var assetRec = new GlideRecord('alm_asset');
  	assetRec.addQuery("purchase_line", pol.sys_id);
  	assetRec.query();
  	if(assetRec.next())
  		return true;
  	else return false;
  },

  createHardwareAssetsPriorToDelivery: function(po){
      var pol = new GlideRecord('proc_po_item');
      pol.addQuery('purchase_order', po.sys_id);
      pol.query();
      while (pol.next()) {
          this.createPOLineHardwareAssetsPriorToDelivery(pol);
      }
  },

  /* get number of in transit assets for the particular POL */
  getNumInTransit: function(pol) {
      var countInTransit = new GlideRecord('alm_asset');
      countInTransit.addQuery('install_status', 9);
      countInTransit.addQuery('purchase_line', pol.sys_id);
      countInTransit.query();
      return countInTransit.getRowCount();
  },

  createPOLineHardwareAssetsPriorToDelivery: function(pol, isStatusPendingAllowed) {
      if (this.hasHardwarePOLineItem(pol, isStatusPendingAllowed)) {
          var count = 0;
          if (this.isHAMPActive || this.isEAMActive) {
              count = this.getNumInTransit(pol);
          }
          var qty = pol.remaining_quantity;
          for (var x = 0; x < (qty - count); x++) {
              this._createAsset(pol.purchase_order, pol, 2);
          }
      }
  },

  poHasPurchaseOrderLineItems: function(po) {
      var pol = new GlideRecord('proc_po_item');
      pol.addQuery('purchase_order', po.sys_id);
      pol.query();
      return pol.hasNext();
  },

  poHasReceivingSlip: function(po) {
      var rsl = new GlideRecord('proc_rec_slip');
      rsl.addQuery('purchase_order', po.sys_id);
      rsl.query();
      return rsl.hasNext();
  },

  polHasReceivingSlipLine: function(po1) {
      var rs = new GlideRecord('proc_rec_slip_item');
      rs.addQuery('purchase_line', po1.sys_id);
      rs.query();
      return rs.hasNext();
  },

  getPOStatusFromPOL: function(polRecord) {
      var gr = new GlideRecord('proc_po');
      if (gr.get(polRecord.purchase_order)) {
          return gr.status;
      }
      return undefined;
  },

  updatePurchaseOrderQuantities: function(rsl) {
      var pol = new GlideRecord('proc_po_item');
      pol.get(rsl.purchase_line);
      var relatedRSL = new GlideRecord('proc_rec_slip_item');
      relatedRSL.addQuery('purchase_line', pol.sys_id);
      relatedRSL.query();
      pol.received_quantity = 0;
      while (relatedRSL.next()) {
          pol.received_quantity += relatedRSL.quantity;
      }
      pol.remaining_quantity = pol.ordered_quantity-pol.received_quantity;
      if (pol.remaining_quantity < 0) {
          pol.remaining_quantity = 0;
      }
      if (pol.remaining_quantity == 0) {
          pol.status = "received";
      }
      pol.update();
  },

  createReceiptSlip: function(po, stockroom) {
      var rs = new GlideRecord('proc_rec_slip');
      rs.initialize();
      rs.received = gs.nowDateTime();
      rs.purchase_order = po.sys_id;
      rs.stockroom = stockroom;
      rs.insert();
      return rs;
  },

  /* Same as Create Asset BR on Receiving Line */
  _selectOrCreateAsset: function(rsl, assetSysIds, swAssetCount, reserve, assetDetails) {
      if (!rsl.purchase_line.nil() &&
              rsl.quantity > 0 &&
              rsl.purchase_line.model.asset_tracking_strategy != 'do_not_track') {
          var type = (new AssetUtils()).getAssetOrConsumable(rsl.purchase_line.model);
          if (type == 'consumable') {
              this.createRecSlipLineConsumables(rsl.purchase_line, rsl, rsl.quantity);
          }
          else if(type == 'software') {
          /* Create software assets with same no of asset details with rights provided with asset details.
          * Rights will get updated as part of Slip line creation */
              this.createRecSlipLineAssets(rsl.purchase_line, rsl, swAssetCount, assetSysIds, reserve, assetDetails);
          } else if (type == 'bundle' && this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.createRecSlipLineBundle) {
              sn_hamp.HAMProcurementUtils.createRecSlipLineBundle(rsl.purchase_line, rsl, parseInt(rsl.quantity), assetSysIds, reserve, assetDetails);
          } else {
              var quantity = parseInt(rsl.quantity);
              this.createRecSlipLineAssets(rsl.purchase_line, rsl, quantity, assetSysIds, reserve, assetDetails);
          }
      }
  },

  /* same as Update Purchase Order Line BR on Receiving Line */
  _updatePOLine: function(rsl) {
      if(!rsl.purchase_order.nil()) {
          this.updatePurchaseOrderQuantities(rsl);
      }
  },

  createReceiptSlipLineWithAssetDetails: function(rec_slip, pol, qty, costWithCurrency, requestedFor, assetDetails, reserve, isSoftwareItem) {
      this.failedAssets = [];
      var assetTable = 'alm_asset';
      var hasAssetPregenerated =  false;
      /* By default create single software asset with rights equals to quantity requested.
       * But user can split the license asset while define asset details, so consider it.*/
      var swAssetCount = 1;
      if (!gs.nil(assetDetails) && assetDetails.length > 1) {
          swAssetCount = assetDetails.length;
      }
      if (isSoftwareItem) {
          assetTable = 'alm_license';
      }
      /* Get sys_ids of details for specific sys_ids, if case of pre-created assets */
      var assetSysids = [];
      for (var i = 0; i < assetDetails.length; i++) {
          if (!gs.nil(assetDetails[i]['sys_id'])) {
              assetSysids.push(assetDetails[i]['sys_id']);
          }
      }
      if (assetSysids.length > 0) {
          hasAssetPregenerated = true;
      }
      var rsl = new GlideRecord('proc_rec_slip_item');
      rsl.initialize();
      rsl.purchase_line = pol.sys_id;
      rsl.requested_for = requestedFor;
      rsl.received = gs.nowDateTime();
      rsl.received_by = gs.getUserID();
      rsl.receiving_slip = rec_slip.sys_id;
      rsl.quantity = qty;
      rsl.setDisplayValue('cost', costWithCurrency);
      rsl.setWorkflow(false); /* Avoid BR getting trigger in case of assets are pre-created */
      var rslId = rsl.insert();
      rsl = new GlideRecord('proc_rec_slip_item');
      rsl.get(rslId);
      /* Handle BRs work */
      this._selectOrCreateAsset(rsl, assetSysids, swAssetCount, reserve, assetDetails);

      rsl.quantity = rsl.quantity - this.failedAssets.length;
      if (parseInt(rsl.quantity) === 0) {
          rsl.deleteRecord();
          return this.failedAssets;
      } else {
          rsl.update();
      }
      this._updatePOLine(rsl);
      /* Handle asset tags, asset serials, and asset reservation */
      var assetGR = new GlideRecord(assetTable);
      assetGR.addQuery('receiving_line', rslId);
      /*  In case of pre-generated asset selection, restrict query to selcted assets only */
      if (!gs.nil(assetSysids) && assetSysids.length > 0) {
          assetGR.addQuery('sys_id', 'IN', assetSysids);
      }
      assetGR.query();
      /* For every asset matching the receiving line (created assets for the POL) update details */
      while (assetGR.next()) {
          var details;
          /* If assets are pre-generated then select asset as per user selection only */
          if (hasAssetPregenerated) {
              details = findAssetDetails(assetGR.sys_id);
          } else { /* else sys ids are attached to assetDetails obj during creation. */
              details = findAssetDetails(assetGR.sys_id);
          }
          if (!gs.nil(details) && !gs.nil(details[0])) {
              updateAsset(assetGR, details[0], reserve, hasAssetPregenerated, requestedFor);
          }
      }
      return this.failedAssets;
      function updateAsset(assetGR, details, reserve, hasAssetPregenerated, requestedFor) {
  		var isHAMPActive = GlidePluginManager.isActive('com.sn_hamp');
          /* Do not update Asset details if the assets are pre-generated, because the user is not allowed to
           * provide any details. It is to avoid unncessary activity logs for respective Asset */
  		// Asset details are updated for pre-generated assets only when purchase order is received through mobile
           if (!hasAssetPregenerated) {
              // Software fields: license_key, rights
              // Asset Tag and Serial Number
              if (details.asset_tag) {
                  assetGR.setValue('asset_tag', details.asset_tag);
              }

              if (details.serial_number) {
                  assetGR.setValue('serial_number', details.serial_number);
              }

              if (isSoftwareItem) {
                  if (details.license_key) {
                      assetGR.setValue('license_key', details.license_key);
                  }

                  if (details.rights) {
                      assetGR.setValue('rights', details.rights);
                  }

                  assetGR.cost = assetGR.receiving_line.cost.getReferenceCurrencyCode() + ';' + String(parseFloat(assetGR.receiving_line.cost.getReferenceValue()) * assetGR.rights);
              }

  			if (!isHAMPActive || !sn_hamp.HAMProcurementUtils || String(assetGR.sys_class_name) !== sn_hamp.HAMProcurementUtils.BUNDLE_TABLE) {
  				if (!gs.nil(details.reserved_for) && !gs.nil(details.reserved_for.value)) {
  					assetGR.setValue('reserved_for', details.reserved_for.value);
  					if (!isSoftwareItem) {
  						assetGR.setValue('substatus', "reserved");
  					}
  				} else if (reserve) {
  					// If reserve toggle in Receive screen is turned on, mark the asset as Reserved
  					assetGR.setValue('reserved_for', requestedFor);
  				}
  			}
          }
          if (isHAMPActive && sn_hamp.HAMProcurementUtils && String(assetGR.sys_class_name) === sn_hamp.HAMProcurementUtils.BUNDLE_TABLE) {
              assetGR.setValue('substatus', "");
          }
  		else if (reserve) {
  			assetGR.setValue('substatus', "reserved");
  		}
          assetGR.salvage_value = pol.model.salvage_value;
          assetGR.depreciation = pol.model.depreciation;
          if (!gs.nil(assetGR.depreciation) && gs.nil(assetGR.depreciation_date)
          && AssetUtils.shouldUpdateDepreciationValues(assetGR.sys_class_name)) {
  			assetGR.depreciation_date = assetGR.receiving_line.received;
  		}
          assetGR.update();
      }
      function findAssetDetails(sysId) {
          var asset;
          /* Find details of specific asset by sys_id */
          for (var i = 0; i < assetDetails.length; i++) {
              if (sysId == assetDetails[i].sys_id) {
                  asset = assetDetails.splice(i, 1);
                  break;
              }
          }
          return asset;
      }
  },

  createReceiptSlipLine: function(rec_slip, pol, qty, costWithCurrency, requestedFor, tags, serials, reserve, isSoftwareItem, rights, licenseKeys) {
      var assetTable = 'alm_asset';
      if (isSoftwareItem) {
          assetTable = 'alm_license';
      }
      var rslGr = new GlideRecord('proc_rec_slip_item');
      rslGr.initialize();
      rslGr.purchase_line = pol.sys_id;
      rslGr.requested_for = requestedFor;
      rslGr.received = gs.nowDateTime();
      rslGr.received_by = gs.getUserID();
      rslGr.receiving_slip = rec_slip.sys_id;
      rslGr.quantity = qty;
      rslGr.setDisplayValue('cost', costWithCurrency);
      var rslId = rslGr.insert();
      // Handle asset tags, asset serials, and asset reservation
      var asset = new GlideRecord(assetTable);
      asset.addQuery('receiving_line', rslId);
      asset.query();
      for (var x = 0; x < qty; x++) {
          var tag = '';
          var serial = '';
          var licenseKey = '';
          if (tags && x < tags.length && tags[x]) {
              tag = tags[x];
          }
          if (serials && x < serials.length && serials[x]) {
              serial = serials[x];
          }
          if (licenseKeys && x < licenseKeys.length && licenseKeys[x]) {
              licenseKey = licenseKeys[x];
          }
          if (asset.next()) {
              if (licenseKey)  { asset.license_key = licenseKey; }
              if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && String(asset.sys_class_name) === sn_hamp.HAMProcurementUtils.BUNDLE_TABLE) {
                  asset.substatus = "";
              }
  			else if (reserve) {
  				asset.substatus = "reserved";
  			}
              if (serial)      { asset.serial_number = serial; }
              if (rights)      { asset.rights = rights; }
              if (tag)         { asset.asset_tag = tag; }
              asset.salvage_value = pol.model.salvage_value;
              asset.depreciation = pol.model.depreciation;
              asset.update();
          }
      }
  	return rslId;
  },

  createReceiptSlipLineForSAMP: function(rec_slip, polGr, pol) {
      var rslGr = new GlideRecord('proc_rec_slip_item');
      rslGr.initialize();
      rslGr.purchase_line = polGr.sys_id;
      rslGr.requested_for = pol.requested_for.sys_id;
      rslGr.received = gs.nowDateTime();
      rslGr.received_by = gs.getUserID();
      rslGr.receiving_slip = rec_slip.sys_id;
      rslGr.quantity = pol.receiving_quantity;
      rslGr.setDisplayValue('cost', pol.cost_with_currency);
      var rslId = rslGr.insert(); // Inserting the Receiving Slip Line will call createRecSlipLineAssets() from a BR
      // Handle asset tag, serial number
      var licGr = new GlideRecord('alm_license');
      licGr.addQuery('receiving_line', rslId);
      licGr.query();
      if (licGr.next()) {
          if (pol.asset_tag) {
          	licGr.asset_tag = pol.asset_tag || "";
          }
          licGr.serial_number = pol.serial_number || "";
          licGr.update();
          // Create license keys defined in the PO receive page
          if (pol.licenseKeys && pol.licenseKeys.length) {
              var lkGr;
              for (var lk = 0; lk < pol.licenseKeys.length; lk++) {
                  lkGr = new GlideRecord('samp_sw_license_key');
                  lkGr.initialize();
                  lkGr.software_entitlement = licGr.sys_id + '';
                  lkGr.license_key = pol.licenseKeys[lk].license_key;
                  lkGr.insert();
              }
          }
      }
  },

  createRecSlipLineConsumables: function(pol, rsl, qty) {

      var po = new GlideRecord('proc_po');
      po.get(pol.purchase_order);

      var rs = new GlideRecord('proc_rec_slip');
      rs.get(rsl.receiving_slip);

      var asset = new GlideRecord('alm_asset');

      asset.addQuery('model', pol.model);

      if (this.isHAMPActive || this.isEAMActive) {
          asset.addQuery('install_status', 2).addOrCondition('install_status', 9);
      } else {
          asset.addQuery('install_status', 2);
      }
      asset.addQuery('purchase_line', pol.sys_id);
      asset.query();

      while (qty > 0) {
          if (asset.next()) {
              if (qty >= asset.quantity) {
                  asset.install_status = 6;
                  asset.substatus = 'available';
                  asset.purchase_line = '';
                  asset.cost = rsl.cost * asset.quantity;
                  asset.stockroom = rsl.receiving_slip.stockroom;
                  asset.update();
                  qty -= asset.quantity;
              } else {
                  this._createConsumable(po, pol, qty, 6, rs.stockroom, rsl);
                  asset.quantity -= qty;
                  asset.update();
                  qty = 0;
              }
          } else {
              this._createConsumable(po, pol, qty, 6, rs.stockroom, rsl);
              qty = 0;
          }
      }
  },


  createRecSlipLineAssets: function(pol, rsl, qty, assetSysIds, reserve, assetDetails) {
      var STATUS_IN_STOCK = 6;
      var STATUS_ON_ORDER = 2;
      var STATUS_IN_USE = 1;
      var STATUS_IN_TRANSIT = 9;
      var po = new GlideRecord('proc_po');
      po.get(pol.purchase_order);

      var type = (new AssetUtils()).getAssetOrConsumable(pol.model);
      var asset = new GlideRecord('alm_asset');
      asset.addQuery('purchase_line', pol.sys_id);
      asset.addQuery('receiving_line', '');

      if (this.isHAMPActive || this.isEAMActive) {
          asset.addQuery('install_status', STATUS_ON_ORDER).addOrCondition('install_status', STATUS_IN_TRANSIT);
      } else {
          asset.addQuery('install_status', STATUS_ON_ORDER);
      }

      /* Incase of asset are precreated and */
      if (!gs.nil(assetSysIds) && assetSysIds.length > 0) {
          asset.addQuery('sys_id', 'IN', assetSysIds.join(','));
      }
      asset.query();
      var itemNumber = 0;

      while (qty > 0) {
          if (asset.next()) {
              if (type == 'software') {
                  asset.install_status = STATUS_IN_USE;
              } else {
                  asset.install_status = STATUS_IN_STOCK;
                  asset.substatus = 'available';
              }
              asset.receiving_line = rsl.sys_id;
              asset.stockroom = rsl.receiving_slip.stockroom;
              if (JSUtil.nil(reserve)) {
                  asset.reserved_for = rsl.requested_for;
              }
              asset.cost = rsl.cost;
  			if (type == 'hardware' || type == 'asset') {
  				if (!gs.nil(assetDetails) && !gs.nil(assetDetails[itemNumber])) {
  					if (assetDetails[itemNumber]['serial_number']) {
  						asset.serial_number = assetDetails[itemNumber]['serial_number'];
  					}
  					if (assetDetails[itemNumber]['asset_tag']) {
  						asset.asset_tag = assetDetails[itemNumber]['asset_tag'];
  					}
  				}
  			}
              asset.work_notes = gs.getMessage('Received through Purchase Order {0} by {1}', [(this.getHref(po, po.number)), (gs.getUser().getFullName())]);
              asset.update();
              qty--;
          } else {
              if (type == 'software') {
                  this._createSoftware(po, pol, STATUS_IN_USE, rsl, reserve, itemNumber, assetDetails);
                  qty--;
              } else if (type == 'hardware' || type == 'asset') {
                  this._createAsset(po, pol, STATUS_IN_STOCK, rsl, reserve, itemNumber, assetDetails);
                  qty--;
              }
          }
          itemNumber++;
      }
  },

  cancelProcurementOrders: function(scRequest) {
      if (scRequest.sourceable) {
          var requestedItem = new GlideRecord('sc_req_item');
          requestedItem.addQuery('request', scRequest.getUniqueValue());
          requestedItem.query();
          while (requestedItem.next()) {
              var requestedItemId = requestedItem.getUniqueValue();
              this._cancelPurchaseOrderLines(requestedItemId);
              this._cancelTransferOrderLines(requestedItemId);
          }
      }
  },

  _cancelPurchaseOrderLines: function(requestedItemId) {
      var purchaseOrderLine = new GlideRecord('proc_po_item');
      purchaseOrderLine.addQuery('request_line', requestedItemId);
      purchaseOrderLine.query();
      while (purchaseOrderLine.next()) {
          if (purchaseOrderLine.getValue('status') != 'received') {
              purchaseOrderLine.setValue('status', 'canceled');
              purchaseOrderLine.update();
          }
      }
  },

  _cancelTransferOrderLines: function(requestedItemId) {
      var transferOrderLine = new GlideRecord('alm_transfer_order_line');
      transferOrderLine.addQuery('request_line', requestedItemId);
      transferOrderLine.query();
      while (transferOrderLine.next()) {
          var stage = transferOrderLine.getValue('stage');
          if (stage != 'received' && stage != 'delivered' && stage != 'in_transit') {
              transferOrderLine.setValue('stage', 'cancelled');
              transferOrderLine.update();
          }
      }
  },

  _createConsumable: function(po, pol, qty, status, stockroom, rsl) {
      var asset = new GlideRecord('alm_consumable');
      asset.quantity = qty;
      asset.model = pol.model;
      // asset model category is consumable
      asset.model_category = '218323293743100044e0bfc8bcbe5d61';
      
     if ( this.isEAMActive && sn_eam.EAMSourcingAutomationAPI && sn_eam.EAMSourcingAutomationAPI.populatePOdetailsOnConsumable) {
  			  // added hook point to set consumables into 'In Stock' state and 'reserved' substate , if sourced through EAM Sourcing or using a eam product catalog
  		sn_eam.EAMSourcingAutomationAPI.populatePOdetailsOnConsumable(pol, asset);
      }
  	else if (!gs.nil(pol.model.cmdb_model_category) && this.isEAMActive && sn_eam.EnterpriseModelUtils && sn_eam.EnterpriseModelUtils.isInEAMmodelHierarchy(pol.model.sys_class_name + '')) {
  		// keeping this block as some customer might be on EAM 2.0 with Utah release and then hook point might not be available, in that case this block executes 
          // asset model category is the first model category of the model for enterprise models
          var modelCategories = pol.model.cmdb_model_category;
          var modelCategoriesArr = modelCategories.split(',');
          asset.model_category = modelCategoriesArr[0];
          // Populate purchase_line, request_line, and requested_for fields for enterprise-class consumable
          asset.purchase_line = pol.sys_id;
          asset.request_line = pol.request_line;
          if ((sn_eam.StockOrderUtils.isStockOrderItem && !sn_eam.StockOrderUtils.isStockOrderItem(pol.request_line.cat_item.sys_id)) && !gs.nil(pol.request_line.requested_for)) {
              // Skip for stock orders
              asset.reserved_for = pol.request_line.requested_for;
              asset.substatus = 'reserved';
          }
  	}
      
      asset.install_status = status;
      if (status == 2) {
          asset.purchase_line = pol.sys_id;
      }
      if (stockroom) {
          asset.stockroom = stockroom;
      }
      if (rsl) {
          asset.cost = rsl.cost * qty;
      }
      asset.insert();
  },

  _createAsset: function(po, pol, status, rsl, reserve, itemNumber, assetDetails) {
      //Respect "Don't create assets" option
      if (pol.model.asset_tracking_strategy == "do_not_track") {
          return;
      }

      //Avoid creating asset if there's no category
      if (!pol.model.cmdb_model_category) {
          var message = gs.getMessage("No Assets for \"{0}\" were created because the Product Model does not have a Model Category", pol.model.display_name);
          gs.addInfoMessage(message);
          return;
      }
      var assetClass = 'alm_asset';
      var detected_model_category = pol.model.cmdb_model_category.split(',')[0];
      var model_category_gr = new GlideRecord('cmdb_model_category');
      model_category_gr.get(detected_model_category);
      if (!gs.nil(model_category_gr.asset_class)) {
          assetClass = model_category_gr.asset_class;
      }
      var asset = new GlideRecord(assetClass);
      asset.initialize();
      asset.model = pol.model;
      asset.install_status = status;
      if (JSUtil.nil(reserve)) {
          asset.reserved_for = po.requested_by;
      }
      asset.purchase_line = pol.sys_id;
      asset.request_line = pol.request_line;
      asset.model_category = detected_model_category;
      asset.vendor = po.vendor;
      asset.acquisition_method = 'purchase';
      var isReservedForSet = 0;
      if (!gs.nil(assetDetails) && !gs.nil(assetDetails[itemNumber])) {
          if (assetDetails[itemNumber]['serial_number']) {
              asset.serial_number = assetDetails[itemNumber]['serial_number'];
          }
          if (assetDetails[itemNumber]['asset_tag']) {
              asset.asset_tag = assetDetails[itemNumber]['asset_tag'];
          }
          if (!gs.nil(assetDetails[itemNumber]['reserved_for']) && !gs.nil(assetDetails[itemNumber]['reserved_for']['value'])) {
              isReservedForSet = 1;
              asset.reserved_for = assetDetails[itemNumber]['reserved_for']['value'];
              asset.substatus = 'reserved';
          }
      }
      if (rsl) {
          if (isReservedForSet === 0) {
              if (JSUtil.nil(reserve)) {
                  asset.reserved_for = rsl.requested_for;
              }
              if (reserve === true) {
                  asset.substatus = 'reserved';
                  if (!JSUtil.nil(rsl.requested_for)) {
                      asset.reserved_for = rsl.requested_for;
                  }
              }
          }
          asset.receiving_line = rsl.sys_id;
          asset.cost = rsl.cost;
          asset.stockroom = rsl.receiving_slip.stockroom;
          asset.work_notes = gs.getMessage('Received through Purchase Order {0} by {1}', [(this.getHref(po, po.number)), (gs.getUser().getFullName())]);
      }
      var assetId = asset.insert();
      if (gs.nil(assetId)) {
          var obj = {};
          obj[pol.sys_id] = assetDetails[itemNumber];
          this.failedAssets.push(obj);
      } else {
  		if (!gs.nil(assetDetails) && !gs.nil(assetDetails[itemNumber])) {
  			assetDetails[itemNumber]['sys_id'] = assetId;
  		}
  	}
  },

  _createSoftware: function(po, pol, status, rsl, reserve, itemNumber, assetDetails) {

      var asset = new GlideRecord('alm_license');
      asset.initialize();
      asset.model = pol.model;
      asset.install_status = status;
      asset.reserved_for = po.requested_by;
      asset.purchase_line = pol.sys_id;
      asset.request_line = pol.request_line;
      asset.vendor = po.vendor;
      asset.acquisition_method = 'purchase';
      if (rsl) {
          asset.receiving_line = rsl.sys_id;
          asset.reserved_for = rsl.requested_for;
          asset.cost = rsl.cost.getReferenceCurrencyCode() + ';' + String( parseFloat(rsl.cost.getReferenceValue()) * rsl.quantity );
          asset.stockroom = rsl.receiving_slip.stockroom;
          /* By default Receiving line quantity is the S/W asset rights unless the user split the license further. */
          if (this.isSAMSActive) {
              asset.software_model = pol.model;
              asset.purchased_rights = rsl.quantity;
              asset.metric_group = pol.metric_group;
              asset.license_metric = pol.license_metric;

              //check if this is for Microsoft Per Core or Per Core(with CAL)
              if ((pol.metric_group.sys_id == this.METRIC_GROUP_MICROSOFT) &&
                  ((pol.license_metric.sys_id == this.LICENSE_METRIC_MS_PER_CORE) || (pol.license_metric.sys_id == this.LICENSE_METRIC_MS_PER_CORE_WITH_CAL))) {
                  if ((rsl.quantity % pol.rights_per_license_pack) === 0) {
                      asset.rights_per_license_pack = pol.rights_per_license_pack;
                      asset.number_of_packs = rsl.quantity / pol.rights_per_license_pack;
                  } else {
                      asset.rights_per_license_pack = 1;
                      asset.number_of_packs = rsl.quantity;
                  }
              }

              asset.salvage_value = pol.model.salvage_value || "";
              asset.depreciation = pol.model.depreciation || "";
              asset.unit_cost = rsl.cost.getReferenceCurrencyCode() + ';' + String( parseFloat(rsl.cost.getReferenceValue()));
              this._setAssetLicenseType(asset, pol);
          } else {
              asset.rights = rsl.quantity;
          }

          asset.work_notes = gs.getMessage('Received through Purchase Order {0} by {1}', [ (this.getHref(po, po.number)), (gs.getUser().getFullName()) ]);
        }
      var assetSysId = asset.insert();
  	if (!gs.nil(assetSysId) && !gs.nil(assetDetails) && !gs.nil(assetDetails[itemNumber])) {
  		assetDetails[itemNumber]['sys_id'] = assetSysId;
  	}

      //Create Downgrade Rights once asset is created
      this._createDowngradeRights(assetSysId, pol.purchase_order);

      //Auto allocate the rights after entitlement creation
      this._autoAllocateRights(assetSysId,rsl,pol);
  },

  _setAssetLicenseType: function(asset, pol) {

      if(!GlidePluginManager.isActive('com.snc.samp')) {
          return;
      }
      // License metric for subscription software
      var USER_SUBSCRIPTION = "48c5d8d293200300544814f1b47ffb45";
      var roGr = new GlideRecord('samp_remediation_option');
      roGr.addQuery('purchase_order', pol.purchase_order);
      roGr.addQuery('maintenance', true);
      roGr.query();

      if (roGr.next()) {
          var microsoftCoreCompany = new SAMCoreCompanyUtil().resolveCoreCompanyForSoftwarePublisher(ReconciliationConstants.MICROSOFT_PUBLISHER_SYS_ID );
          if(pol.model.manufacturer.toString() === microsoftCoreCompany) {
              asset.setValue('product_type', 'perpetual_software_assurance');
          } else {
              asset.setValue('product_type', 'perpetual_maintenance');
          }
          asset.setValue('start_date', new GlideDateTime().getDate());
          asset.setValue('end_date', new GlideDateTime('9999-12-31'));
      } else if (pol.license_metric == USER_SUBSCRIPTION) {
          asset.setValue('product_type', 'subscription');
          asset.setValue('start_date', new GlideDateTime().getDate());
      }
  },

  _createDowngradeRights: function(assetSysId, poSysId) {
      if (!this.isSAMSActive) {
          return;
      }
      var roGr = new GlideRecord('samp_remediation_option');
      roGr.addQuery('purchase_order', poSysId);
      roGr.addNotNullQuery('license_metric_result');
      roGr.query();

      if(roGr.next()) {
          var downgradeRights = roGr.license_metric_result.downgrade_rights;
          if(!gs.nil(downgradeRights)) {
              var downgradeModels = downgradeRights.split(',');
              var dmGr = new GlideRecord('samp_downgrade_model');
              for(var model in downgradeModels) {
                  dmGr.initialize();
                  dmGr.setValue('license', assetSysId);
                  dmGr.setValue('model', downgradeModels[model]);
                  dmGr.insert();
              }
          }
      }
  },

  _autoAllocateRights: function(assetSysId, rsl, pol){
  	if(!GlidePluginManager.isActive('com.snc.samp') || String(pol.request_line.cat_item.workflow) !== '917506260711301019cc909f0ad3005d') {
          return;
      }

  	var rightsToAllocate = rsl.quantity >= pol.request_line.quantity?pol.request_line.quantity:rsl.quantity;
  	var seGr = new GlideRecord('alm_license');
  	if(seGr.get(assetSysId)){
  		var entitlementType = String(seGr.license_metric.entitlement_type);
  		if(entitlementType === 'user' && !gs.nil(rsl.requested_for)){
  			this._createAllocations(assetSysId, rightsToAllocate, 'alm_entitlement_user','assigned_to', rsl.requested_for);
  		}else if(entitlementType === 'workstation'){
  			var device = this._getDeviceId(pol.request_line.cat_item, pol.request_line, rsl.requested_for);
  			if(!gs.nil(device))
  				this._createAllocations(assetSysId, rightsToAllocate,'alm_entitlement_asset','allocated_to', device);
  		}
  	}
  },

  _getDeviceId: function(catItemSysId, ritmSysId, requestedUser){
  	var deviceId = null;
  	var varGr = new GlideRecord('item_option_new');
  	varGr.addQuery('name', 'device_name');
  	varGr.addQuery('cat_item',catItemSysId);
  	varGr.query();
  	varGr.next();

  	var gr = new GlideRecord('sc_item_option_mtom');
  	gr.addQuery('request_item',ritmSysId);
  	gr.query();
  	while(gr.next()) {
  		if (String(gr.sc_item_option.item_option_new) === varGr.getUniqueValue()) {
  			deviceId = gr.sc_item_option.value;
  			if(!gs.nil(deviceId)){
  				var deviceGr = new GlideRecord('cmdb_ci_computer');
  				if(deviceGr.get(deviceId)){
  					var user = deviceGr.getValue('assigned_to');
  					if(user === String(requestedUser))
  						return deviceId;
  				}
  			}
  		}
  	}
  	return null;
  },


  _createAllocations: function(seSysId, rightsUsed, table, column, allocationTo) {
  	var almSysId = '';
  	var almGr = new GlideRecord(table);
  	almGr.addQuery(column, allocationTo);
  	almGr.addQuery('licensed_by', seSysId);
  	almGr.query();

  	if (almGr.next()) {
  		var newQuantity = parseInt(almGr.getValue('quantity'), 10) + rightsUsed;
  		almGr.setValue('quantity', newQuantity);
  		almSysId = almGr.getUniqueValue();
  		almGr.update();
  	} else {
  		var newAlmGr = new GlideRecord(table);
  		newAlmGr.initialize();
  		newAlmGr.licensed_by = seSysId;
  		newAlmGr.setValue('quantity', rightsUsed);
  		if(column === 'assigned_to')
  			newAlmGr.assigned_to = allocationTo;
  		else if(column === 'allocated_to')
  			newAlmGr.allocated_to = allocationTo;
  		almSysId = newAlmGr.insert();
  	}
  },

  _findPO: function(item, vendor, consolidate, destination) {
      var request = new GlideRecord('sc_request');
      request.get(item.request);
      var po = new GlideRecord('proc_po');
      po.addQuery('vendor', vendor);
      po.addQuery('ship_to', destination);
      po.addQuery('status', 'requested');
      if (consolidate == false) {
          po.addQuery('init_request', request.sys_id);
      }
      po.query();
      if (po.next()) {
          return po;
      }
      else {
          return this.createPO(request, vendor, destination);
      }
  },

  getHref: function(record, text) {
        return '[code]<a href=' + record.getLink(true) + '>' + String(text) + '</a>[/code]';
  },

  /* ******************************************************
  UI Action condition checkers
  ****************************************************** */
  showCancelOnPurchaseOrder: function(po) {
      var HAMAllows = true;
      if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showCancelOnPurchaseOrder) {
          var response = new sn_hamp.HAMProcurementUtils().showCancelOnPurchaseOrder(po);
          HAMAllows = response.answer;
          if (!response.continueProcessing) { return HAMAllows; }
      }

      var ITAMCommonAllows = true;
      if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showCancelOnPurchaseOrder) {
          response = new sn_itam_common.ContractRenewalUtils().showCancelOnPurchaseOrder(po);
          ITAMCommonAllows = response.answer;
          if (!response.continueProcessing) { return ITAMCommonAllows; }
      } 
      if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.showCancelOnPurchaseOrder) {
          response = new sn_itam_common.AssetCommonProcurementUtils().showCancelOnPurchaseOrder(po);
          ITAMCommonAllows = response.answer;
          if (!response.continueProcessing) { return ITAMCommonAllows; }
      }

      var status = po.getValue('status');
      var statusAllows = (status === 'requested' || status === 'ordered'
                          || status === 'pending' ||  status === 'suspended');
      return HAMAllows && statusAllows && !this.isAssetOperationInProgress(po);
  },

  showReceiveInIEOnPurchaseOrder: function(po) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showReceiveInIEOnPurchaseOrder) {
  		var response = new sn_hamp.HAMProcurementUtils().showReceiveInIEOnPurchaseOrder(po);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showReceiveInIEOnPurchaseOrder) {
  		response = new sn_itam_common.ContractRenewalUtils().showReceiveInIEOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var status = po.getValue('status');
  	var statusAllows = (status === 'ordered' || status === 'pending');
  	var browserAllows = (gs.getSession().getProperty('user_agent_browser') == 'ie'
  						 && gs.getSession().getProperty('user_agent_version') < 10);
  	return HAMAllows && statusAllows && browserAllows && !this.isAssetOperationInProgress(po);
  },

  showReceiveOnPurchaseOrder: function(po) {
  	var HAMAllows = true;
  	var response;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showReceiveOnPurchaseOrder) {
  		response = new sn_hamp.HAMProcurementUtils().showReceiveOnPurchaseOrder(po);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.showReceiveOnPurchaseOrder && sn_itam_common.AssetCommonProcurementUtils.prototype.usePOReceiveForLeaseContract && new sn_itam_common.AssetCommonProcurementUtils().usePOReceiveForLeaseContract(po) === true) {
  		response = new sn_itam_common.AssetCommonProcurementUtils().showReceiveOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	} else if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showReceiveOnPurchaseOrder) {
  		response = new sn_itam_common.ContractRenewalUtils().showReceiveOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var status = po.getValue('status');
  	var statusAllows = (status === 'ordered' || status === 'pending');
  	var browserAllows = !(gs.getSession().getProperty('user_agent_browser') == 'ie'
  						  && gs.getSession().getProperty('user_agent_version') < 10);
  	return HAMAllows && statusAllows && browserAllows && !this.isAssetOperationInProgress(po);
  },

  showHardwareCreationLinkOnPurchaseOrder: function(po) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showHardwareCreationLinkOnPurchaseOrder) {
  		var response = new sn_hamp.HAMProcurementUtils().showHardwareCreationLinkOnPurchaseOrder(po);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.showHardwareCreationLinkOnPurchaseOrder && sn_itam_common.AssetCommonProcurementUtils.prototype.usePOReceiveForLeaseContract && new sn_itam_common.AssetCommonProcurementUtils().usePOReceiveForLeaseContract(po) === true) {
  		response = new sn_itam_common.AssetCommonProcurementUtils().showHardwareCreationLinkOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	} else if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showHardwareCreationLinkOnPurchaseOrder) {
  		response = new sn_itam_common.ContractRenewalUtils().showHardwareCreationLinkOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var statusAllows = (po.getValue('status') === 'ordered');
  	var hasHwPO = this.hasHardwarePO(po);
  	return HAMAllows && statusAllows && hasHwPO && !this.isAssetOperationInProgress(po);
  },

  showOrderOnPurchaseOrder: function(po) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showOrderOnPurchaseOrder) {
  		var response = new sn_hamp.HAMProcurementUtils().showOrderOnPurchaseOrder(po);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showOrderOnPurchaseOrder) {
  		response = new sn_itam_common.ContractRenewalUtils().showOrderOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var procurementIntegrationAllows = true;
  	if (this.procurementIntegrationActive) {
  		procurementIntegrationAllows = new sn_asset_proc_int.ITAMProcurementIntegrationUtil().showOrderOnPurchaseOrder(po);
  	}

  	var status = po.getValue('status');
  	var statusAllows = (status === 'requested' || status === 'canceled');
  	var hasPOLs = this.poHasPurchaseOrderLineItems(po);
  	return HAMAllows && procurementIntegrationAllows && statusAllows && hasPOLs && !this.isAssetOperationInProgress(po);
  },

  showDeleteOnPurchaseOrder: function(po) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showDeleteOnPurchaseOrder) {
  		var response = new sn_hamp.HAMProcurementUtils().showDeleteOnPurchaseOrder(po);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showDeleteOnPurchaseOrder) {
  		response = new sn_itam_common.ContractRenewalUtils().showDeleteOnPurchaseOrder(po);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var canDelete = (po.isValidRecord() && po.canDelete());
  	var poHasReceivingSlip = this.poHasReceivingSlip(po);
  	return HAMAllows && canDelete && !poHasReceivingSlip && !this.isAssetOperationInProgress(po);
  },

  showCancelOnPurchaseOrderLine: function(pol) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showCancelOnPurchaseOrderLine) {
  		var response = new sn_hamp.HAMProcurementUtils().showCancelOnPurchaseOrderLine(pol);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showCancelOnPurchaseOrderLine) {
  		response = new sn_itam_common.ContractRenewalUtils().showCancelOnPurchaseOrderLine(pol);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var status = pol.getValue('status');
  	var statusAllows = (status === 'requested' || status === 'ordered'
  						|| status === 'pending' ||  status === 'suspended');
  	return HAMAllows && statusAllows && !this.isAssetOperationInProgress(pol);
  },

  showOrderOnPurchaseOrderLine: function(pol) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showOrderOnPurchaseOrderLine) {
  		var response = new sn_hamp.HAMProcurementUtils().showOrderOnPurchaseOrderLine(pol);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showOrderOnPurchaseOrderLine) {
  		response = new sn_itam_common.ContractRenewalUtils().showOrderOnPurchaseOrderLine(pol);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

      if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.showCancelOnPurchaseOrderLine) {
          response = new sn_itam_common.AssetCommonProcurementUtils().showCancelOnPurchaseOrderLine(pol);
          ITAMCommonAllows = response.answer;
          if (!response.continueProcessing) { return ITAMCommonAllows; }
      }

  	var statusAllows = (pol.getValue('status') === 'canceled');
  	var poStatus = String(this.getPOStatusFromPOL(pol));
  	var poStatusAllows = ((poStatus !== 'canceled') && (poStatus !== 'received'));
  	return HAMAllows && statusAllows && poStatusAllows && !this.isAssetOperationInProgress(pol);
  },

  showHardwareCreationLinkOnPurchaseOrderLine: function(pol) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.showHardwareCreationLinkOnPurchaseOrderLine) {
  		var response = new sn_hamp.HAMProcurementUtils().showHardwareCreationLinkOnPurchaseOrderLine(pol);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.showHardwareCreationLinkOnPurchaseOrderLine && sn_itam_common.AssetCommonProcurementUtils.prototype.usePOReceiveForLeaseContract && new sn_itam_common.AssetCommonProcurementUtils().usePOReceiveForLeaseContract(pol.purchase_order) === true) {
  		response = new sn_itam_common.AssetCommonProcurementUtils().showHardwareCreationLinkOnPurchaseOrderLine(pol);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	} else if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.showHardwareCreationLinkOnPurchaseOrderLine) {
  		response = new sn_itam_common.ContractRenewalUtils().showHardwareCreationLinkOnPurchaseOrderLine(pol);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var statusAllows = (pol.getValue('status') === 'ordered');
  	var hasHwPOL = this.hasHardwarePOLineItem(pol);
  	var assetTrackingAllows = (String(pol.model.asset_tracking_strategy) !== 'do_not_track');
  	return HAMAllows && statusAllows && hasHwPOL && assetTrackingAllows && !this.isAssetOperationInProgress(pol);
  },

  shouldCreateAssetsFromRSL: function(rsl) {
  	var HAMAllows = true;
  	if (this.isHAMPActive && sn_hamp.HAMProcurementUtils && sn_hamp.HAMProcurementUtils.prototype.shouldCreateAssetsFromRSL) {
  		var response = new sn_hamp.HAMProcurementUtils().shouldCreateAssetsFromRSL(rsl);
  		HAMAllows = response.answer;
  		if (!response.continueProcessing) { return HAMAllows; }
  	}

  	var ITAMCommonAllows = true;
  	if (this.isITAMCommonActive && sn_itam_common.AssetCommonProcurementUtils && sn_itam_common.AssetCommonProcurementUtils.prototype.shouldCreateAssetsFromRSL && sn_itam_common.AssetCommonProcurementUtils.prototype.usePOReceiveForLeaseContract && new sn_itam_common.AssetCommonProcurementUtils().usePOReceiveForLeaseContract(rsl.purchase_line.purchase_order) === true) {
  		response = new sn_itam_common.AssetCommonProcurementUtils().shouldCreateAssetsFromRSL(rsl);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	} else if (this.isITAMCommonActive && sn_itam_common.ContractRenewalUtils && sn_itam_common.ContractRenewalUtils.prototype.shouldCreateAssetsFromRSL) {
  		response = new sn_itam_common.ContractRenewalUtils().shouldCreateAssetsFromRSL(rsl);
  		ITAMCommonAllows = response.answer;
  		if (!response.continueProcessing) { return ITAMCommonAllows; }
  	}

  	var baseConditionAllows = !rsl.purchase_line.nil() && rsl.quantity > 0
  		&& rsl.purchase_line.model.asset_tracking_strategy != 'do_not_track';
  	return HAMAllows && baseConditionAllows;
  },

  rollupAssetOperationToPO: function(po) {
  	var assetOperationInProgress = '';
  	var pol = new GlideRecord('proc_po_item');
  	pol.addQuery('purchase_order', po.sys_id);
  	pol.addNotNullQuery('asset_operation');
  	pol.query();
  	while (po.next()) {
  		if (pol.asset_operation) {
  			assetOperationInProgress = pol.asset_operation;
  			break;
  		}
  	}
  	var purchaseOrder = new GlideRecord('proc_po');
  	purchaseOrder.get(po.sys_id);
  	purchaseOrder.asset_operation = assetOperationInProgress;
  	purchaseOrder.update();
  },

  getAssetOperationInProgressMessage: function() {
  	return gs.getMessage('Asset updates are in progress. Please do not update this record until complete. Reload page to check progress.');
  },

  isAssetOperationInProgressForPO: function(po) {
  	var purchaseOrder = new GlideRecord('proc_po');
  	purchaseOrder.get(po.sys_id);
  	return !!record.getValue('asset_operation');
  },

  isAssetOperationInProgressForPOL: function(po) {
  	var pol = new GlideRecord('proc_po_item');
  	pol.addQuery('purchase_order', po.sys_id);
  	pol.addNotNullQuery('asset_operation');
  	pol.query();
  	if (pol.hasNext()) {
  		return true;
  	}
  	return false;
  },

  isAssetOperationInProgress: function(record) {
  	var isPOLine = record.getTableName() === ProcurementUtils.PURCHASE_ORDER_LINE_ITEM_TABLE;
  	var result = !!record.getValue('asset_operation');
  	if (!isPOLine) {
  		return result || this.isAssetOperationInProgressForPOL(record);
  	} else {
  		return result || this.isAssetOperationInProgressForPO(record.purchase_order);
  	}
  },

  updateAssetOperationInProgress: function(record, value) {
  	record.asset_operation = value;
  	record.update();
  },

  areAssetsAlreadyCreatedForPOL: function(pol){
  	var orderedQuantity = pol.ordered_quantity;
  	var asset = new GlideAggregate('alm_asset');
  	asset.addQuery('purchase_line', pol.sys_id);
  	asset.addAggregate('COUNT');
  	asset.query();
  	if (asset.next()) {
  		var count = asset.getAggregate('COUNT');
  		if (count === orderedQuantity){
  			return true;
  		}
  	}
  	return false;
  },

  type: 'ProcurementUtils'
};
ProcurementUtils.getCloseTaskCondition = function(task) {
  return parseInt(task.state, 10) !== -5 && task.state < 3
  	&& task.approval.toString() !== 'requested';
};
ProcurementUtils.getConsumeAndCloseTaskCondition = function(task) {
  var baseTaskCondition = global.ProcurementUtils.getCloseTaskCondition(task);
  return baseTaskCondition && task.task_name.toString() === 'consume' && !gs.nil(task.model)
      && !gs.nil(task.quantity) && !gs.nil(task.stockroom) && !gs.nil(task.requested_for);
};
ProcurementUtils.getConsumble = function (model, fromStockroom, status, substatus, quantity) {
  var con = new GlideRecord('alm_consumable');
  con.addQuery('stockroom', fromStockroom.sys_id);
  con.addQuery('quantity', '>=', quantity);
  con.addQuery('model', model.sys_id);
  con.addQuery('install_status', status);
  con.addQuery('substatus', substatus);
  global.AssetUtils.addAssetQuery(con, global.AssetUtils.ASSET_FUNCTION_FEATURE.SOURCING);
  con.setLimit(1);
  con.query();
  if (con.next()) {
  	return con;
  }
  throw gs.getMessage('{0} consumable in State - {1} and Subsate - {2} with {3} or more quantity'
  	+ ' not found in {4} stockroom', [model.display_name, status, substatus, quantity, fromStockroom.name]);
};
ProcurementUtils.consumeTaskAssetQualifier = function (asset) {
  var gr = new GlideRecord('alm_asset');
  gr.addQuery('model', asset.model);
  gr.addQuery('stockroom', asset.stockroom);
  gr.addQuery('install_status', global.AssetUtils.INSTOCK_STATUS);
  gr.addQuery('substatus', global.AssetUtils.AVAILABLE_SUBSTATUS);
  global.AssetUtils.addAssetQuery(gr, global.AssetUtils.ASSET_FUNCTION_FEATURE.SOURCING);
  return gr.getEncodedQuery();
};
ProcurementUtils.reserveAndReleaseAsset = function (currentTask, previousTask) {
  if (gs.nil(currentTask.asset)) {
  	gs.addErrorMessage(gs.getMessage('The following mandatory fields are not filled in: {0}',
  		currentTask.asset.getLabel()));
  	return false;
  }
  if (gs.nil(previousTask.asset)) {
  	gs.addErrorMessage(gs.getMessage('Existing value of {0} field is empty on task',
  		previousTask.asset.getLabel()));
  	return false;
  }
  var currentAssetGr = new GlideRecord(currentTask.asset.sys_class_name);
  var previousAssetGr = new GlideRecord(previousTask.asset.sys_class_name);
  currentAssetGr.get(currentTask.asset.sys_id);
  previousAssetGr.get(previousTask.asset.sys_id);
  if (currentAssetGr.getValue('install_status') !== global.AssetUtils.INSTOCK_STATUS.toString()
  	|| currentAssetGr.getValue('substatus') !== global.AssetUtils.AVAILABLE_SUBSTATUS) {
  	gs.addErrorMessage(gs.getMessage('Selected asset is not available in stock.'));
  	return false;
  }
  currentAssetGr.install_status = global.AssetUtils.INSTOCK_STATUS;
  currentAssetGr.substatus = global.AssetUtils.RESERVED_SUBSTATUS;
  currentAssetGr.reserved_for = previousAssetGr.reserved_for;
  currentAssetGr.update();

  previousAssetGr.install_status = global.AssetUtils.INSTOCK_STATUS;
  previousAssetGr.substatus = global.AssetUtils.AVAILABLE_SUBSTATUS;
  previousAssetGr.reserved_for = '';
  previousAssetGr.update();
  return true;
};
ProcurementUtils.isRITMFullyReceived = function (ritm) {
  var reqItemGr = new GlideRecord('sc_req_item');
  reqItemGr.get(ritm);
  // If the requested item haven't been fully sourced, it can't be fully received at this point
  if (!reqItemGr.sourced) {
  	return false;
  }

  // Local order - consume asset task
  var cTaskGr = new GlideRecord(global.ProcurementUtils.CONSUME_ASSET_TASK_TABLE);
  cTaskGr.addQuery('parent', ritm);
  cTaskGr.addQuery('active', true);
  cTaskGr.setLimit(1);
  cTaskGr.query();
  if (cTaskGr.hasNext()) {return false;}

  // Transfer order - transfer order line
  var tolGr = new GlideRecord(global.ProcurementUtils.TRANSFER_ORDER_LINE_TABLE);
  tolGr.addQuery('request_line', ritm);
  tolGr.addQuery('stage', 'NOT IN', 'delivered,cancelled');
  tolGr.setLimit(1);
  tolGr.query();
  if (tolGr.hasNext()) {return false;}

  // Purchase order - purchase order line item
  var polGr = new GlideRecord(global.ProcurementUtils.PURCHASE_ORDER_LINE_ITEM_TABLE);
  polGr.addQuery('request_line', ritm);
  polGr.addQuery('status', 'NOT IN', 'received,canceled');
  polGr.setLimit(1);
  polGr.query();
  if (polGr.hasNext()) {return false;}

  // Enterprise local order - confirm asset task
  if (new TableUtils('sn_eam_confirm_asset_task').tableExists()) {
  	var cfTaskGr = new GlideRecord('sn_eam_confirm_asset_task');
  	cfTaskGr.addQuery('parent', ritm);
  	cfTaskGr.addQuery('state', '1');
  	cfTaskGr.setLimit(1);
  	cfTaskGr.query();
  	if (cfTaskGr.hasNext()) {return false;}
  }

  return true;
};
ProcurementUtils.validateFieldsForConsumeAsset = function (consumeAssetTask) {
  consumeAssetTask.state = 3;
  if (!consumeAssetTask.update()) {
  	return consumeAssetTask;
  }
  return consumeAssetTask.parent.getRefRecord();
};
ProcurementUtils.canCloseConsumeTask = function (consumeAssetTask) {
  var hasAccess = gs.getUser().hasRole('inventory_user') || gs.getUser().hasRole('itil') || gs.getUser().hasRole('admin') || gs.getUser().hasRole('asset');
  return (hasAccess && (global.ProcurementUtils.getCloseTaskCondition(consumeAssetTask) && consumeAssetTask.task_name.toString() !== 'consume'));
};

Sys ID

378ec0bb37c13000158bbfc8bcbe5d17

Offical Documentation

Official Docs: