The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Overwriting CatalogPriceCalculator script include

viktor_lukashov
Giga Contributor

The Wiki mentions that the ServiceNow default service catalog pricing model can be extended should a need be. However it does not provide much information on how this can be done. In the community there have been questions about that every once in a while but they tend to stay without answers. I've posted this same question to HI and then did some trial and error myself. Below is a summary of a working method how to extend the pricing model (keep in mind that it is not an official guide).

First, backup the OOB versions of the CatalogPriceCalculator and CatalogRecurringPriceCalculator script includes. You are going to overwrite them with custom versions so it's a good idea to keep OOB copies available in case you would need to restore them in some future upgrade. Set the 'active' flag to false on the both OOB script includes and keep them without any other modifications.

The idea is to create two custom fields on the catalog item table to be able to specify custom catalog price calculation scripts and to change the default scripts to check for these fields and either run OOB or custom calculations. Two different fields are necessary because the price calculation is divided into two parts: one for purchase price and another for recurring price.

The screenshots below show the definition of the 2 custom fields sc_cat_item.u_custom_price_calculator and sc_cat_item.u_custom_recurring_price_calculator. Both fields are references to the Script Include table.

u_custom_price_calculator.png

u_custom_recurring_price_calculator.png

The custom copy of the CatalogPriceCalculator script include looks as below. It checks if the custom field u_custom_price_calculator is defined on the catalog item and if so, tries to use the price calculator from that script include. If for any reason the custom price calculator class is not found, the OOB class is used instead (renamed to DefaultCatalogPriceCalculator).

On the ServiceNow versions starting from Fuji the system property glide.sc.use_custom_pricegenerator should be set to true in order to enable pricing calculations via script includes. When it's false all pricing calculations are handled inside ServiceNow in Java code completely out of reach for customization.

Note that when defining a custom price calculator class, its name should match the name of the script include where it is defined. An example will come further into the post.

// save a reference to the global scope

var __global = this;

/*

* For a given cart item, this routine will return the price

*/

var CatalogPriceCalculator = Class.create();

CatalogPriceCalculator.prototype = {

    initialize: function(/* GlideRecord */ gr) {

          var customPriceCalculatorSI = gr.cat_item.u_custom_price_calculator;

          if (customPriceCalculatorSI) {

                // gs.include() should define a class in the global scope

                // the class name shoudl be the same as the script include name

                gs.include(customPriceCalculatorSI.name);

                var CatalogPriceCalculatorImpl = __global[customPriceCalculatorSI.name];

          }

          if (typeof CatalogPriceCalculatorImpl !== 'function') {

                // by default use the out-of-the-box price calculator

                CatalogPriceCalculatorImpl = DefaultCatalogPriceCalculator;

          }

          this.cpc = new CatalogPriceCalculatorImpl(gr);

    },

    calcPrice : function() {

          return this.cpc.calcPrice();

    }

};

/*

* This is a [renamed] copy of the OOB CatalogPriceCalculator class.

*/

var DefaultCatalogPriceCalculator = Class.create();

DefaultCatalogPriceCalculator.prototype = {

  < full copy of the OOB CatalogPriceCalculator prototype goes here >

};

The custom copy of the CatalogRecurringPriceCalculator follows the same idea with the only difference being the name of the custom field on the catalog item.

All what has been explained so far was the preparation work. It does not actually change any price calculation logic on any catalog items. The actual custom pricing logic should be defined in a new script include (you can defined as many pricing models as necessary).

Here is an example (create a new script include)

  • Name: DynamicServerOrderPriceCalculator
  • API Name: global.DynamicServerOrderPriceCalculator
  • Client Callable: false
  • Application: Global
  • Accessible from: This application scope only

gs.include("CatalogPriceCalculator");

var DynamicServerOrderPriceCalculator = Class.create();

DynamicServerOrderPriceCalculator.prototype = Object.extendsObject(DefaultCatalogPriceCalculator, {

    calcPrice : function() {

          var base_price = this.cartItem.cat_item.price.getReferenceValue();

          var capacity = this.cartItem.variables['u_capacity'].getGlideObject().getValue();

          return 0.75 * base_price + 0.25 * capacity;

    }

});

To put this custom pricing script in use, it should be referenced from the u_custom_price_calculator field on a catalog item definition.

12 REPLIES 12

Florian7
Mega Contributor

Dear Viktor,



Many thanks for this informations !



I've used your solution and I've had a problem with the new script includes (DynamicServerOrderPriceCalculator) because in this state, he don't work.
But with the following version, it works fine




gs.include("CatalogPriceCalculator");



var DynamicServerOrderPriceCalculator = Class.create();


DynamicServerOrderPriceCalculator.prototype = Object.extendsObject(DefaultCatalogPriceCalculator, {


        initialize: function(/* GlideRecord */ gr) {


                  this.cartItem = gr;


        },


       


        calcPrice: function() {


                  var base_price = this.cartItem.cat_item.price.getReferenceValue();


                  var capacity = this.cartItem.variables['u_capacity'].getGlideObject().getValue();


                  return 0.75 * base_price + 0.25 * capacity;


        },


       


        type : DynamicServerOrderPriceCalculator


});



Have a nice week,


Thank you again !


Hi Florian and Viktor,



I am new to ServiceNow and was wondering how you would call this script include within a Client Script. I know you need to use a GlideAjax but for some reason I can't seem to do it.



Any help would be highly appreciated.



Thanks



Max


Hi Max,



Catalog price calculation is a specific part of ServiceNow functionality which is designed and built to be used on the server side. ServiceNow automatically invokes these scripts on the server side every time when a catalog shopping cart is updated and displays an updated price in the cart. As far as I know, that's their sole purpose (nothing to do with the client-side).



In this context the question you've posted provokes me to ask for some additional details of your need.



If you need to call price calculation scripts from the client side manually, it probably means that you are not that much using the OOB ServiceNow functionality. That's totally OK, but then you can define a regular client-callable script include as described in the wiki and do not need to follow the specific example outlined in this topic.



Note, that if you have some common code which you'd like to use both in your custom catalog price calculation scripts and in the client-callable ajax-processor, you could move it into a separate script include and reference it from both.



// Viktor


Hi Viktor,



First of all thank you for your quick reply.



Basic Problem:


What I am trying to achieve is the ability to multiply a field (on a form) by a certain amount and add that cost to the price of the cart item.



Possible Solution:


I have customized the script include from Florian as follows:



SCRIPT INCLUDE:


gs.include("CatalogPriceCalculator");  


 


var DynamicServerOrderPriceCalculator = Class.create();  


DynamicServerOrderPriceCalculator.prototype = Object.extendsObject(CatalogPriceCalculator, {  


        initialize: function(/* GlideRecord */ gr) {  


                  this.cartItem = gr;  


        },  


           


        calcPrice: function() {  


                  var base_price = this.cartItem.cat_item.price.getReferenceValue();  


    var capacity = this.getParameter('sysparm_pricecalc');


    var total = base_price + capacity;


    return total;  


        },  


           


        type : 'DynamicServerOrderPriceCalculator'


});  



CLIENT SCRIPT:



function onChange(control, oldValue, newValue, isLoading) {


  if (isLoading || newValue == '') {


  return;


  }


  //infra_vm_gb is the amount of GB you want to order


  var amountstring = g_form.getValue('infra_vm_gb');


  var amountnumber = Number(amountstring);


  //we pay 0.55 cents (this price changes depending on other options and we will create further logic later)


  //per GB and would like to add that price to the cart.


  var amounttotal = amountnumber * 0.55;



  var ga = new GlideAjax('DynamicServerOrderPriceCalculator');


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


  ga.addParam('sysparm_pricecalc',amounttotal);


  ga.getXML(myanswer);



  function myanswer(response) {


  var answer = response.responseXML.documentElement.getAttribute("answer");


  alert(answer);


  }



}



However this is not working.



Any help would be appreciated.



Thanks



Max