How to create a catalog item with a user specified cost

jamesmcwhinney
Giga Guru

Background:

We are trying to replace our old online capital expenditure request form by using service now.

For some of the use cases, predetermined catalog items like laptops and cell phones will be available in the catalog and can be added to the cart.

However, the majority of the use cases require the user to be able to manually specify line items and descriptions.

Unfortunately, the Service Catalog Variable Pricing - ServiceNow Wiki capability does not allow full-on user entered pricing.

At best, this would allow us to offer a reference box containing a predetermined set of prices and allow the user to choose from those.

Workaround for user entered variable pricing via an actual text box:

First, I created a new table called "user prices" to hold a set of user entered prices.

The table has two columns:

  • "User Cart" which is a reference to the sc_cart table.
  • "u_price" which is an integer field to hold a custom cost.

Next, I created a new catalog item "Manually entered expenses" with 4 text field variables to store the user entered costs for 4 line items.

I also added a reference field variable called u_price with the label "Total Cost" which is a reference to the new "user prices" table and has the "Pricing Implications" box checked.

(Checking the pricing implications box will cause the catalog to add the related u_price value to the total cost of this item).

UserEnteredPricing.JPG

We now have a catalog item that will allow a user to choose the cost of the item from the list of costs in that table.

But we want the user to be able to just type in 4 costs and use the total of those instead....

...So the next step is to create a catalog client script to total those 4 costs up, create a record in the new "user prices" table, and set the total cost box to the new record we created.

I created a catalog client script "Manual Expense Save" as follows:

Name: "Manual Expense Save"

Type: onSubmit

Applies to: catalog item

Catalog item: (Our catalog item "Manually entered expenses")

Applies on Catalog Item view: Yes

Applies on Requested Items: No

Applies on Catalog Tasks: No

Catalog Client Script: "Manual Expense Save"

function onSubmit() {

      //Get Current User

  var UserSysID = g_user.userID;

  //Get Costs from form

  var Cost1 = g_form.getIntValue('variables.item1_cost');

  var Cost2 = g_form.getIntValue('variables.item2_cost');

  var Cost3 = g_form.getIntValue('variables.item3_cost');

  var Cost4 = g_form.getIntValue('variables.item4_cost');

  var TotalCost = Cost1 + Cost2 + Cost3 + Cost4;

  //Add a new record in the custom table (cart id, cost)

  var ga = new GlideAjax('AJAXOtherExpense');

  ga.addParam('sysparm_name','setCatItemCost');

  ga.addParam('sysparm_totalcost',TotalCost);

  ga.addParam('sysparm_usersysid',UserSysID);

  ga.getXMLWait();

  var ReferenceID = "" + ga.getAnswer();

  g_form.setValue('variables.u_price',ReferenceID);

}

Now we need to write a script include to handle for the server side.

This will get the users current cart, and then add a record to the new "user prices" table with the total price of the 4 user entered costs.

It will then return the sys_id of the new row generated, so that the client script can use it to set the reference box to that value.

Script Include: AJAXOtherExpense

var AJAXOtherExpense = Class.create();

AJAXOtherExpense.prototype = Object.extendsObject(AbstractAjaxProcessor, {

  setCatItemCost: function() {

  //Get Users Cart

  var UserSysID = this.getParameter('sysparm_usersysid');

  var CartID = '0';

  var gr = new GlideRecord('sc_cart');

  gr.addQuery('user', UserSysID);

  gr.query();

  while (gr.next()) {

  CartID = gr.getValue('sys_id');

  }

  //Add Total Cost to available Costs and return the sysID of the new row

  var TotalCost = this.getParameter('sysparm_totalcost');

  var gr = new GlideRecord('u_smse_catalogitem_prices');

  gr.initialize();

  gr.u_price = TotalCost;

  gr.u_cart = CartID;

  return gr.insert();

    }

});

We now have full-on user specified pricing for our catalog item.   But we have an ugly "Total Cost" variable which we may not want the user to see or mess with.

To hide the total cost field, we need to create another catalog client script "Manual Expense Onload" to hide the total cost variable:

Name: "Manual Expense Onload"

Type: OnLoad

Applies on Catalog Item View: Yes

Applies on Requested Items: Yes

Applied on Catalog Tasks: No

Catalog Client Script: Manual Expense Onload

function onLoad() {

  g_form.setDisplay('variables.u_price', false);

}

Its a hack for sure,.. but its the best workaround Ive been able to come up with so far.

If anyone has any other suggestions for a better way to go about it Id love to hear them.

Note that I included a reference to the cart in the "user prices" table so that the table could be cleared out periodically if needed without affecting any carts that were still active.

OrderItem.jpg

Checkout.JPG

7 REPLIES 7

kevinray
Giga Expert

I submitted the enhancement request back in Decenber of 2016 and it's still open with no comments


did u find a solution for this

jamesmcwhinney
Giga Guru

7 years later, and to my knowledge there still isn't an OOTB way to handle this requirement.

The solution I posted above is still working, but in hindsight it now seems overcomplicated.
A far simpler solution was proposed in the below thread:
Populate RITM Price Field with Catalog String Variable - Developer Community - Article - ServiceNow ...

This could be adapted into a more generic solution:

  1. Adding a field to the "sc_cat_item" table called "Total Price Override Variable Name" (u_total_price_override_variable_name)
  2. In your catalog item, choose the variable you want to store your calculations and hold the total for the cart item.  (ex. "item_total") and populate that into the new "Total Price Override Variable Name" field for your catalog item.
  3. Create a business rule on the sc_req_item table "Override Total Price" that fires only on insert, and only when current.cat_item.u_total_price_override_variable_name is not blank.
    (function executeRule(current, previous /*null when async*/) {
    	var reqAmount = current.variables["" + current.cat_item.u_total_price_override_variable_name];
    	var num = reqAmount.toString();
    	var cost = parseFloat(num);
    	current.price = "CAD;"+ cost;
    })(current, previous);

The tradeoff is that with the old (complex) solution, I was technically still using the OOTB pricing logic.

With this simpler solution, we are overriding it, which might be risky in certain circumstances....