Pricing engine

Using Wolfram Language to calculate the price of 
Wolfram Products

Jon McLoone
Wolfram Europe

The problem

Our systems business systems need modernization

Our pricing has combinatorial complexity

Mathematica alone currently has around 10,000 potential prices

Fragmented authority

The opportunity

Expose (and eliminate) inconsistent rules

Increase commercial agility

Make it easier to roll out new products and license types

Make it easier to do promos and other experiments

Solution

Symbolic representation of products requests

priceItemLocal[<|
"Product"->"Mathematica",
"Region"->LinguisticAssistant,
"UseClass"->"Commercial",
"ServiceType"->"PremierService",
"TimeCommitment"->"Perpetual",
"AccessControl"->"SingleMachine",
"DeliveryMode"->"Download",
"Quantity"->1|>]

Symbolic representation product catalog

Product

<|
    "Dimension" -> "Product",
    "Name" -> "Mathematica",
    "Parent" -> "MathematicaProductsClass",
    "BasePrice" -> Quantity[1048, IndependentUnit["WolframCurrencyUnit"]],
"Contents"->{...}
|>,

Software class

<|"Name"->"SoftwareClass","Dimension"->"Product","Parent"->"ProductDefaults","AllowService"->True,"CostOfGoodsFunction"->Quantity[0,IndependentUnit["WolframCurrencyUnit"]],(*For now software is assumed to be 100% profit*)"PriceWCUFunction"->(Round[Block[{serviceRequest},
With[{
timePeriodScaling=TimePeriodScaling[#],
perpProdPrice=
KeyKeyLookupInherited[#,"Product","BasePrice"]*KeyKeyLookupInherited[#,"UseClass","PriceMultiplier"]*KeyKeyLookupInherited[#,"AccessControl","PriceMultiplier"]*KeyKeyLookupInherited[#,"DeliveryMode","PriceMultiplier"]*KeyKeyLookupInherited[#,"TimeCommitment","PriceMultiplier"]},serviceRequest=Join[KeyTake[#,{"Quantity","UseClass","AccessControl","DeliveryMode","StartDate","EndDate","TimeCommitment"}],<|"Product"->Lookup[#,"ServiceType",PricingFailure[<|"Reason"->"MissingValue","key"->"ServiceType"|>]],"ParentProductPriceWCU"->perpProdPrice|>];
Which[!TrueQ[KeyKeyLookupInherited[#,"Product","AllowService"]],perpProdPrice*timePeriodScaling,!FreeQ[serviceRequest,_PricingFailure],FirstCase[serviceRequest,_PricingFailure,Infinity],serviceRequest["Product"]==="None",perpProdPrice*timePeriodScaling,True,perpProdPrice*timePeriodScaling+priceItemWCU[serviceRequest]]]],0.001]&),"Visible"->False|>,

Product defaults

<|"Name"->"ProductDefaults",
"Dimension"->"Product",
"ComponentContribution"->1.,
(*Keys not in use yet*)
"Visible"->False,
"VersionNumber"->"None",
"ERPInformation"->"None",
"ComponentContribution"->1,"RoundingFunction"->(NameRecordLookupInherited["Rounding","StandardRounding","RoundingFunction",#]&),(*some products use round01*)
"ItemCode"->"GenericSoftware",
"UpgradePolicy"->"None",
"AllowService"->False,"BaseCostOfGoods"->Quantity[10,IndependentUnit["WolframCurrencyUnit"]],"BasePrice"->PricingFailure[<|"Reason"->"BasePriceNotFound"|>],"CostOfGoodsFunction"->(Lookup[#,"ItemPriceWCU"]&),(*Non-software products are assumed to have 0% Gross margin*)"ERPInformation"->"Allocatable"|>,

Product class map

class[name_String]:=Lookup[First@Select[$PricingDatabase,#Name===name&],"Dimension"];
classColor[name_]:=Style[name,If[class[name]==="ProductClass",Red,Black]];
productGraph=Graph[If[MissingQ@Lookup[#,"Parent"],Nothing,
DirectedEdge[classColor@#Name,classColor@Lookup[#,"Parent"]]
]&/@Select[$PricingDatabase,#Dimension==="Product"||#Dimension==="ProductClass"&],VertexLabels->"Name"]

Symbolic representation of business rules

<|"Dimension"->"AccessControl","Name"->"SingleMachine","Priority"->10,"PriceMultiplier"->1|>,

<|"Dimension"->"AccessControl","Name"->"Network","Priority"->10,"PriceMultiplier"->1.3|>,
...
<|"Dimension"->"UseClass","Name"->"Student","Priority"->20,"Parent"->"Commercial","Restrictions"-><|"Product"->"CloudOnlyProductsClass"|>|>,

<|"Dimension"->"UseClass","Name"->"Student","Priority"->10,"PriceMultiplier"->0.07,"Restrictions"-><|"AccessControl"->("SingleMachine"|"WolframID")|>|>,

Promos

<|"Dimension"->"Discounts","Name"->"MathematicaBOGOF","Dimension"->"Discounts","Parent"->"PriceProportionDiscount","Restrictions"-><|"Product"->"Mathematica","PromoCode"->PromoCodeMatchQ["MathematicaBOGOF"]|>,"Discount"->ItemVolumeDiscountFunction[2,0.5],"DiscountReason"->"BOGOF"|>,

“Purchase” involves augmenting the request object

basket=productRequestExpand/@priceBasket[<|
"Product"->"Mathematica",
"Region"->LinguisticAssistant,
"UseClass"->"Educational",
"ServiceType"->"PremierService",
"TimeCommitment"->"Perpetual",
"AccessControl"->"Network",
"DeliveryMode"->"Download",
"Quantity"->2|>]

LicenseObject[First[basket]]

Deploy as an APIFunction

Lookup[
URLExecute[CloudObject[MouseOver],
{"basket"->ExportString[List@<|"Product"->"Mathematica",
"Region"->"UnitedKingdom",
"UseClass"->"Educational",
"AccessControl"->"Network",
"DeliveryMode"->"Download",
"ServiceType"->"PremierService",
"Quantity"->1,
"TimeCommitment"->"Perpetual"|>,"JSON"]},VerifySecurityCertificates -> False],
"ResellerExtendedPriceLocal"]

Other issues

Executing configured products

Time & payment schedules

Basket optimization

Service

Repositories

Trust and permission and external clients

<<PricingEngine`;
Begin["PricingEngine`Private`"];