All calculation tasks required upon calculating baskets or orders will be done by this framework. Generally, the calculation is triggered during the checkout process as well as when adding or removing items from or to the basket. The present concept is addressed to developers who want to become familiar with the basic structure of this framework.
The idea behind this framework is the spreadsheet functionality. The values are stored in cells, and you can define rules that will be executed when the cells are calculated.
Simple example:
In this example, the cell B will be calculated with the rule X. Once the values for cell B are available, the cell C can be calculated through rule Y. So the whole calculation can be defined only with the cells and the rules. All rules and cells will be defined in a rule set.
One goal of this framework is to handle the complexity of very extensive basket calculations. In the past, there was a large pipeline, which was difficult to handle and extend. To overcome these problems, the basket calculation framework has been introduced.
Term | Description |
---|---|
Cell | The value of each cell should be immutable. That means that a cell can be written by only one rule. If you need a further manipulation of this cell, you must store it in a new cell via another rule.
|
Computed item | The result of a rule delivers computed items which will be stored in the cells. Each computed item knows its dependent computed items in order to get the transparency about how the calculation yields these results. For each item, you can navigate to all items produced in former steps up to the start of the calculation. The computed items do not know the rules that produced them. This information is available only in the rule set. |
PII | Payment instrument info |
PLI | Product line item |
Result view | The result view is an abstraction of the calculation results, which will get all relevant data through methods with corresponding names. |
Rule | The rule itself describes the calculation steps between one or more cells. It has source cells, which make up the input of the rule, and target cells, which describe the result of the calculation step. There are also some exceptions, which will be discussed later. Rules can only be executed if the source cell values are available (i.e., are already calculated). If one value is missing, the rules cannot be executed. The source cells and the target cells can be related to different groups or sub groups. For example, a rule can have 3 groups as input and 2 groups as output (see group selector). |
Rule set | The rule set is a container for all rules and cells. It is defined in a single Java class. |
The Result Set defines all Rules and Cell Selectors. The Rules themselves use the Cell Selectors to create the Computed Items. The Cell Selectors are used to get the Computed Items in the calculation. The Result View defines methods to get the results. All BO classes (e.g,, the OrderBO) use the Result View to retrieve the calculated values.
The cell and the group as selector are easy to understand, but the SubGroup is a little more sophisticated.
Consider the following scenario:
Assume, there is a basket with four line items. Each line item represents a cell in a group called RootLineItem. The ID of the cell in this group is, for instance, also the UUID of the line item object. Now you can easily select the computed items (created during the calculation).
Now we want to calculate two or more taxes for each line item. At the creation time we do not know how many taxes will be there, so a group would not help here. Each tax needs a unique ID, but the ID is not unique here, except we specifically create the ID, which requires a difficult algorithm to resolve the correct taxes of the line item.
For that case, we can create a sub group of a given computed item. The SubGroup helps to create and to select child items that belong to one parent. In our case, we can create many taxes for one line item.
It is also possible to create a sub group in a sub group and so on. This way, you can map even complex scenarios.
Every rule set must match the following criteria:
The computed items will be used to store the results of the rule execution.
The interface ComputedItem is the base interface for all computed items.
Attribute | Description |
---|---|
Description | Gets the description for this ComputedItem instance |
ID | Returns the ID of this ComputedItem. The ID is unique in the group. |
Group | Returns the group this ComputedItem belongs to |
SpreadSheet | Returns the SpreadSheet instance this item belongs to |
DependsOn | Returns all ComputedItem instances this item depends on |
DependsOn(group) | Returns the ComputedItem instances this item depends on and that belong to the specified group |
The interface ComputedMoneyItem passes on the amount of money.
Attribute | Description |
---|---|
Money | Returns the money amount as instance of the type Money or null if no money amount is set for this instance |
Attribute | Description |
---|---|
ComputedProductLineItems | Returns all computed product line item instances which belong to this shipping bucket |
RegionID | Returns a set of the IDs of all Regions |
ShippingMethodID | Returns the ID of the ShippingMethod |
Attribute | Description |
---|---|
ShippingRule | Returns the shipping rule |
ShippingRuleID | Returns the shipping rule ID |
ShippingRuleDisplayName | Returns the name of the shipping rule |
ShippingRuleDescription | Returns the description of the shipping rule |
The interface ComputedProductLineItem encapsulates a ProductLineItem.
Attribute | Description |
---|---|
ProductLineItemUUID | Returns the UUID of the product line item this instance depends on, or null if not set |
ProductLineItem | Returns the product line item instance, with the UUID set at this instance, or null if no UUID is set |
ProductRef | Returns ProductRef instance representing the product this instance refers to |
Attribute | Description |
---|---|
Quantity | Returns the quantity |
The interface ComputedDiscountItem stores the discounts.
Attribute | Description |
---|---|
PromotionID | Gets the promotion ID |
DiscountDescription | Gets the discount description |
DiscountRebate | Gets the discount rebate |
DiscountType | Gets the discount type |
The interface ComputedPaymentInstrumentItem stores the payment instrument item.
Attribute | Description |
---|---|
PaymentInstrumentInfo | Gets the payment instrument info |
The interface ComputedPercentageItem stores the percentage item.
Attribute | Description |
---|---|
Percentage | Returns the value representing a percentage |
The interface ComputedServiceLineItem stores the service line item.
Attribute | Description |
---|---|
ServiceLineItemUUID | Returns the UUID of the service line item this instance depends on, or null if not set |
ServiceLineItem | Returns the service line item instance, with the UUID set at this instance, or null if no UUID is set |
The calculation rules are doing the real calculation. They use the input, transform it, and finally write the outcome into the target cells. Besides the input and target cells, the rule considers the current com.intershop.component.spreadsheet.capi.CalculationContext
. This can contain important attributes which influences the way a rule processes the data. The context is built each time a re-calculation of the basket is required. To hook into this preparation step, ICM has the pipeline extension point ProcessBasketCalculation-PrepareCalculationContextData
.
Each rule has to implement the base class com.intershop.component.spreadsheet.capi.CalculationRule
.
Intershop recommends to use the existing rules whenever possible.
The class CreateInitialProductItemsRule
initially creates product line items from the line item container.
The class AddMoneyRule
adds the values of a certain input structure and outputs the result in a new target structure, e. g.,:
Depending on the target structure, the values will be added differently. If you, for instance, pass in a group set and want to specify a group as return value, all sets of each group will be added together (so that you get a sum for each group set). If you specify a cell as return value, all values of all group sets will be added to a single sum.
The class AddQuantitiesRule
adds the quantities.
The class CalculatePaymentCostsRule
calculates the payment costs of the given order amount.
Computes the quantities of all child items in relation to their root items.
If the root item has the quantity of a and the child item the quantity of b, the resulting quantity is a*b.
The class CopyMoneyValueIntoNativeAttributeRule
copies the attributes to the given native targets.
The class CreateInitialServiceItemsRule
creates the initial service items from the given line item container.
The rules CreateWarrantyPricesRuleNet
and CreateWarrantyPricesRuleGross
create the single prices for each warranty. The classes are specific for the price types Net and Gross.
Filters the product line items with the service type flag ExcludeFromShipping
from the computed product line items and returns the new group.
The class PromotionBasketCalculationRule
calculates all discounts and promotions configured in the channel.
The class PromotionCalculationRule
calculates all discounts and promotions configured in the channel. The rule uses the variables
from the calculation context.
This rule is a calculation step within the basket calculation.
It determines the shipping buckets for further shipping cost calculation.
This rule is a calculation step within the basket calculation.
It determines the extra cost applied on each product line item due to surcharges defined with the product or shipping rule.
This rule is a calculation step within the basket calculation.
It determines the bucket cost and shipping PLI cost for each product line item within each shipping bucket.
The class CalculateMoneyRateRule
calculates the percentage values of the given two money items.
This rule is a calculation step within the basket calculation.
It determines the product line items with a price (filters them).
The class MultiplyMoneyWithPercentageRule
multiplies prices with a percentage value.
The class MultiplyMoneyWithQuantityRule
multiplies single prices with given quantities.
The class SubtractMoneyRule
subtracts the given two money items.
This rule determines the product single base price for one or more product line items.
For the (scaled) price lookup, the quantities are accumulated for all line items referring to the same product.
The old net price flag at prices is not taken into account by this rule.
The rule can be configured to perform a lookup for a given currency (that might be different to the purchase currency of the LineItemCtnr).
The actual price lookup is performed via the ProductPriceMgr. If the flag BasePriceFixed
is set for the product line item, the single base price is directly taken from the ProductLineItemPO.
Determines the percentage amount of included taxes for the given gross price and the according tax rate.
Mathematically this means r = (1-(1(1+p))). This expression is calculated via the data type Double. With this the maximum precision is also bound to the Double data type.
The class DeterminePaymentItemTaxRatesRule
determines the tax rate for the given payment elements.
The class DetermineProductItemTaxRatesRule
determines the tax rates of the given PLIs.
The class CalculateTaxSumsPerRateRule
calculates all taxes by its tax rates.
The class RoundMoneyRule
rounds the given money items.
Starting with ICM 13.0.0, the precision to be used can be adjusted by the variable "CalculationRoundingPrecision" available in the CalculationContext
. The value can be configured in the ICM preference of the channel and is interpreted as additional precision on top of the currency based rounding precision.
The class CreateChildProductItemsRule
creates a sub group of product line items.
Child products are sorted by the product UUID.
The class SelectMoneyRule
selects one of two money inputs as output, depending on which one is available. If both are available, the first input will be returned.
The class CollectPromotionDiscountValuesRule
collects promotion discounts with the same line item ID and adds them up. This is necessary because promotion discounts may be split up on individual items. For example, a line item with a quantity of three may include two items with a discount value of $1.00 and one item with a discount value of $1.02. Because values like taxes are calculated on a per-item basis (in the V3 rule sets), they need to be added up at the end to determine the gross value of the line item.
The class DetermineTaxRatesForPromotionDiscountsRule
maps a set of given tax rates to associated promotion discounts. This is necessary because promotion discounts may be split up on individual items. For example, a line item with a quantity of three may include two items with a discount value of $1.00 and one item with a discount value of $1.02. In this scenario the first "sub item" in the calculation may have an ID of "0-0" and the second one "0-2" (where the first number is always the associated line item). Tax rates, on the other hand, are always associated with line items, and because they use different IDs (in this scenario it would simply be "0"), a mapping is needed, which is what this rule does.
The class CalculateAverageTaxRateRule
calculates the average tax rate from all line items and the individual tax rates. The tax rate is calculated based on the base price distribution of the line items, i.e., items with a higher price have a higher weight when calculating the average tax rate.
The class ChangeMoneySignRule
changes the sign of a money item, i.e., multiplies it with -1. This is used to compare certain calculation values with discounts (as discounts are always negative).
The class DetermineMaximumMoneyItemRule
determines the maximum from a given set of money items.
The CashbackCalculationRule
calculates the (optional) cashback earning promotion dependent on the current basket, which is represented by the passed computed items.
The BucketCostDistributionRule
distributes costs from surcharges over the given items.
The class CalculateBucketTaxes
calculates the taxes for the given buckets.
This rule sums up all values while considering their relation to the buckets (and their surcharges).
The rule calculates the total shipping discount on bucket or line item container level.
The rule ShippingGroupedChargeCalculatorRule
groups money values by the rule ID of there related ComputedShippingExtraChargeItem.
The class copies the provided amounts to the field "SingleBasePricePC" of the ComputedProductLineItem
list.
The calculation rule DistributeMoneyValueRule
distributes the value of a given ComputedMoneyItem
evenly onto other items.
The class CalculateBaseAmounts
calculates the base amounts for all payment instrument information (PIIs).
The calculation rule CalculatePaymentSurchargesRule
determines payment surcharges (costs and taxes). It uses the context variable "PaymentConfigurations" to load the configuration of the payment cost handling.
The CalculatePIICostsRule
calculates the payment costs for all PaymentInstrumentInfo objects.
This rule sets the amount for the payment attribute at the PaymentInstrumentInfo objects.
This rule deletes (removes) persistent payment instrument objects that are assigned to the basket but not needed anymore (e.g., free gifts not applied anymore).
This rule distributes the basket total amount over the payment instruments. It also divides the payment instruments in two groups (open and limited tender).
The class DistributePaymentCostsRule
determines payment surcharges (cost and taxes). It uses the context variable "PaymentConfigurations" to load the configuration of the payment cost handling.
The calculation rule CalculateTaxSumsPerRateAndBucketRule
groups the shipping cost tax values by their tax rate. The sums are calculated for each bucket.
The rule was added with version ICM 12.2.0.
Intershop provides the option to set up your own desired way of basket calculation via CalculationRulesets and also offers example calculation rule sets, which are used in the demo shop inTRONICS.
For details on how to create your own calculation rule sets, see Cookbook - Basket Calculation.
This section will give you an overview of the existing example calculation rule sets:
NetBasedCalculation_V1
GrossBasedCalculation_V1
The Net and Gross based V2 calculation rule sets are well known and available since IS 7.0. In IS 7.6 we introduced the Net and Gross based V3 calculation rule sets.
Info
Since it is a general requirement for all shops to display money values with their maximum allowed number of positions after decimal point (which is usually 2 for each common currency , e.g., 13.99 USD), rounding of these values will happen at some point.
At the moment the calculation rule sets NetBasedCalculation_V3
and GrossBasedCalculation_V3
are used in the demo store as default but there is always the possibility to change the reference rule sets or to use your own. For registering a new rule set, please have a look at Cookbook - Basket Calculation | Recipe: Register the Rule Set.
V1 was the very first rule set already used in previous Enfinity Shop software and had yet limited features. It was not able to use a mix of GROSS and NET values. If your prices in the database were gross, all displayed and calculated prices in your shop had to be gross as well. This V1 rule set is no longer used, but is preserved for downwards compatibility reasons to handle old orders that were once created with this rule set.
V2 is the default calculation rule set for all Intershop 7.x versions starting with Intershop 7.0.0.0 and before Intershop 7.8.0.0 (with Intershop 7.8.0.0 and newer the default rule set was switched to V3).
Example: In a scenario with net prices in the database and gross prices displayed in the storefront (Net Based Taxation Service, Channel Preference Price Type = Net, Price Display = Gross), the V2 basket calculation works like this:
At the same time, the single gross price of this line item has to be displayed in the storefront (rounded at 2 positions) as well as the total gross price of the number of this line item summed up ( rounded at 2 positions).
Example in numbers:
Disadvantage: The prices displayed in the storefront may not sum up logically.
Advantage: The calculation totals are more precise due to less rounding in between.
In Intershop 7.6, we introduced a new calculation value called 'single discounted base prices'. This is due to the fact that some e-commerce managers wanted to calculate the discount at the level of single products to ensure that the correct discount is provided, e.g., to a taxation service. Furthermore, we are using the 'single discounted base prices' for calculating the PLI net and gross prices by multiplying it with the PLI quantity. Especially in net/gross settings where you store net prices and display those as gross prices, that change prevents the calculation from rounding issues.
V3 is the default calculation rule set for all Intershop 7.8.x versions and newer, starting with Intershop 7.8.0.0.
Example: In a scenario with net prices in the database and gross prices displayed in the storefront (Net Based Taxation Service, Channel Preference Price Type = Net, Price Display = Gross), the V3 basket calculation works like this:
At the same time, the single gross price of this line item has to be displayed in the storefront (rounded at 2 positions) as well as the total gross price of the number of this line item summed up (rounded at 2 positions).
Example in numbers:
Disadvantage: The calculation totals are less precise due to cumulative rounding issues made for intermediate results.
Advantage: The prices displayed in the storefront sum up logically.
It may be beneficial to only have one rule set instead of two different ones for net and gross based calculations. The only difference between the two is the factor by which taxes are calculated and if they are added or subtracted from the "base" price.
Info
Starting with ICM 12.4.1, the possibility to adjust the rounding was introduced.
The behavior is configurable by the variables
CalculationRoundingPrecision
OutputRoundingPrecision
The values are used by the RoundMoneyRule
and interpreted as precision in addition to the decimal places used by the purchase currency. This means that any calculation rule set that uses this rule will utilize the new behavior.
Please be aware that configuring both values differently can lead to inconsistent values presented to the customer. Here a small example where the calculation rounding precision is set to 2 (round to 4 digits):
Output Rounding Precision = 0 | Output Rounding Precision = 2 | ||
---|---|---|---|
Line item net price | $7.6540 | $7.65 | $7.6540 |
Tax | $1.4543 | $1.45 | $1.4543 |
Line item gross price | $9.1083 | $9.11 | $9.1083 |
In case the output rounding precision is set to 0, even this small example becomes inconsistent as the single values are rounded down and their sum rounded up.
A further consideration pertains to the handling of payment provider data. In the event that the payment service connector transmits order details to the provider and the basket is recalculated there, a discrepancy may arise due to rounding, given that most APIs function on a lower (currency) precision. If feasible, the connector configuration to the payment provider should be adjusted to ensure that only the order sums (or the essential data) are transferred.