An item’s original price instead of the current day price is used in the following situations:
When modifying a previously submitted order – This allows the product or SKU prices to remain consistent with the prices that were applied when the order was originally submitted. Original pricing is applied only towards items that were in the original order. Any new products or SKUs that are added to the order will use the current day pricing
When pricing items in an exchange order – This allows the product or SKU prices in the exchange order to remain consistent with the prices that were applied to the original order. As with previously submitted orders, the original pricing is applied only towards items that were in the original order. Any new products or SKUs that are added to the exchange will be priced using the current day pricing
The ItemPriceSource
object allows you to provide pricing information based upon a specific Product/SKU combination. The override is then used in place of the default current day pricing.
This feature also provides the ability to generate a set of ItemPriceSource
objects from the pricing information stored for the items within an order. This generates the source objects for pricing previously submitted orders and exchange orders by generating the source objects from the submitted order and the original purchase order.
ItemPriceSourceHandler Interface
The ItemPriceSourceHandler
interface defines the API for generating ItemPriceSource
objects from previously-priced items and consuming ItemPriceSource
objects during a pricing operation. For example:
public interface ItemPriceSourceHandler { //this method will be called when generating ItemPriceSources from previously //priced items in an Order. A true return value will indicate the handler //modified the ItemPriceSource. public boolean populateItemPriceSource(ItemPriceSource pPriceSource, Order pOrder,CommerceItenm pCommerceItem, CommerceItem pParentCommerceItem); //this method will be called by the individual calculators during a pricing //operation to get the matching ItemPriceSource for the item. public ItemPriceSource getItemPriceSource(ItemPriceInfo pPriceQuote, CommerceItem pItem, RepositoryItem pPricingModel, Locale pLocale, RepositoryItem pProfile, Map pExtraParameters); //this method will be called by the individual calculators during a pricing //operation to determine if the matching ItemPriceSource should be consumed by //the calculator. A false return means the calculator should bypass its normal //processing and immediately return. public boolean shouldConsumeSource(ItemPriceSource pItemPriceSource); }
ItemPriceSourceHandler Implementing Calculators
The following ItemPriceCalculators
and ItemSchemePriceCalculators
implement the ItemPriceSourceHandler
interface. The following calculators are stored in /atg/commerce/pricing/calculators
:
Calculator Type | Calculator |
---|---|
|
|
|
|
|
|
Generating ItemPriceSources from ItemPriceInfos
PricingTools
provides the API for generating ItemPriceSources
from an order. It instantiates an ItemPriceSource
object for each CommerceItem
and SubSkuItem
in the order, as well as calling each ItemPriceSourceHandler
to populate the calculator-specific values of the ItemPriceSource
.
Each calculator provides a PricingAdjustment
that details how the item price was adjusted by the calculator. These objects can be used to determine the original cost basis used by the calculator. Each PricingAdjustment
created by a calculator contains a description specific to the calculator and each ItemPriceSourceHandler
extracts their PricingAdjustment(s)
from the ItemPriceInfo
by matching the description.
For example, the standard list price calculator adds an adjustment:
PricingAdjustment adjustment = new PricingAdjustment(Constants.LIST_PRICE_ADJUSTMENT_DESCRIPTION, null, getPricingTools().round(adjustAmount), quantity);
The Constants.LIST_PRICE_ADJUSTMENT_DESCRIPTION
is a key to a resourced description in the atg.commerce.pricing.resources
file that is unique to the calculator.
In the implementation of populateItemPriceSource
, the calculator looks for an adjustment with the same resourced description:
public boolean populateItemPriceSource(pItemPriceSource, pOrder, pCommerceItem, pParentItem pItemPriceInfo) { for each pItemPriceInfo PriceAdjustment { if(adjustment.getAdjustmentDescription.equals(Constants.LIST_PRICE_ADJUSTMENT_ DESCRIPTION)) { double list = round(adjustment.getAmount() / adjustment.getQuantity()); pItemPriceSource.setListPrice(list); return true; } } }
Price List-Based ItemPriceSourceHandlers
Item calculators that delegate to scheme calculators for pricing also delegate to the scheme calculators when generating an ItemPriceSource
.
The ItemPriceCalculator
implementation of populateItemPriceSource
iterates over each of its configured scheme calculators. Each scheme calculator that implements the ItemPriceSourceHandler
interface is called through its populateItemPriceSource
interface. The ItemPriceSource
priceScheme
property is set by the main calculator to the scheme that returns true
from populateItemPriceSource
.
The following describes how each scheme’s handler uses the PricingAdjustments
to determine its original cost basis and populate the ItemPriceSource
.
List Price Scheme
The ItemPriceSourceHandler
for the ListPrice
scheme sets the ItemPriceSource
list price based on the single PricingAdjustment
.
Bulk Price Scheme
Volume-based schemes use a list of volume levels as their cost basis. Each level defines a unit cost for a quantity. The ItemPriceSourceHandler
determines the volume levels from the single PricingAdjustment
added by the ItemBulkPriceCalculator
. The generated list of volume levels contains only one entry.
For example, if the PricingAdjustment
has amount=100
, quantity=2
, only one pricing level will be created:
Qty | Price | Equation |
---|---|---|
1 | 50 | Quantity = 1 (First level is always set to one) |
When performing bulk pricing, the calculator only adds one pricing adjustment to the item, so the system can recreate only one level of the scheme from the adjustments. For example, if the pricing levels are defined as:
Qty | Price |
---|---|
1 | 50 |
3 | 40 |
6 | 30 |
When the item quantity is 3, the original item is priced at $120 or 3 items at $40 each. Because the system generates a single level of 1 at 40, it cannot recreate the additional calculations of the 1 at 50 or 6 at 30 levels.
This may cause several issues to occur. For example, if the order is modified from a quantity of 3 to a quantity of 10, the additional 7 items are set at 40. Additionally, if the order is modified to a quantity of 1, the item is then priced at 40, not 50, since the system does not know the original first level.
This situation can also occur in returns. If the customer returns 2 of the original 3 items, the system cannot calculate the proper $10 adjustment for the 1 remaining item. As such, the net result is an $80 refund instead of a $70 refund, and the customer will receive the 1 item at $40 instead of $50.
Tiered Price Scheme
The ItemPriceSourceHandler
for this volume-based scheme generates a list of volume levels using the PricingAdjustments
added by the ItemTieredPriceCalculator
.
For example, if the original pricing levels are defined as:
Qty | Price |
---|---|
1 | 50 |
3 | 40 |
6 | 30 |
The PricingAdjustments
for an item quantity of three (3) would be generated as:
Qty | Adjustment |
---|---|
2 | 100 |
1 | 40 |
The ItemPriceSources
list of volume levels contains one entry for each PricingAdjustment
found:
Qty | Price | Equation |
---|---|---|
1 | 50 | Quantity = 1 (First level is always set to one) |
3 | 40 | Quantity = Previous |
The original pricing adjustments for tiered pricing may only be sufficient to generate a partial list of volume levels. The volume levels cannot be reconstructed up to the level covered by the item quantity originally priced. For example, if the levels are defined as: 1 at 50, 3 at 40, 6 at 30 and the item quantity is 3, the system can only calculate the levels to be 1 at 50 and 3 at 40. The last level, 6 at 30, cannot be determined from the available data stored in the item’s PricingAdjustments
.
When modifying the same order with a quantity of 3 to a total quantity of 10, the shopper will not receive the $30 unit price for the last 4 items. The system will set the new 7 items at the $40 unit price.
In the case of an exchange, if you exchange 1 unit for 8 more units of the same item, the net result is the exchange order will identify the new 7 items at the $40 unit price.
Volume Price Levels
Volume-based ItemPriceSourceHandlers
that generate a list of volume levels create new transient priceLevel
repository items for each level it calculates from the PricingAdjustments
. Creating repository items enables the substitution of the ItemPriceSource
list of levels with the list normally attached to the price repository item.
The repository IDs for transient items are generated by an instance of the TransientIdGenerator
, which is dynamically created through a property of the volume-based ItemPriceSourceHandlers
.
Sale Price Handlers
Sale price versions of the handlers calculate the original sale prices from the order and ItemPriceSource
. The ItemPriceSourceHandlers
that generate sale price information require two sources of information to calculate the original sale price:
Their
PricingAdjustment
(s), which detail the sale calculator original adjustments to the list priceThe
ItemPricingOveride
that contains the list price value
When determining the sale price, the SKU-based sale price handler refers to the ItemPriceSource
list price and PricingAdjustment
amount.
For example, calculating an original item (qty1) with a list price of $20 and a sale price of $15 would be calculated when the SKU-based sale price handler executes, setting the ItemPriceSource
list price to $20. The PricingAdjustment
contains an amount = (5.00)
, quantity 1
. These two values are used to calculate a sale price of $15.
(ItemPriceSource List Price + (PricingAdjustment.amount /
PricingAdjustment.quantity)) = Sales Price
(20 + ((5.00) / 1)) = 15
Note that a list price is not a requirement for calculating sale prices. In the following example, the PricingAdjustment
contains amount=15
, resulting in the following calculation:
(ItemPriceSource List Price + (PricingAdjustment.amount /
PricingAdjustment.quantity)) = Sales Price
(0 + (15 / 1)) = 15
Note: Price list-based sales price handlers use the same logic as the SKU-based version.
Price List-Based Volume Sale Price Handlers
To generate the sale price volume levels, the list price volume levels and the PricingAdjustment
(s) are used to calculate the appropriate values.
For example, if the original tier levels are defined:
List Price Qty | List Price | Sale Price Qty | Sale Price |
---|---|---|---|
1 | 50 | 1 | 45 |
3 | 40 | 3 | 35 |
6 | 30 | 6 | 25 |
The PricingAdjustments
for an item quantity of three (3) are generated as:
List Price Qty | List Price Adjustment | Sale Price Qty | Sale Price Adjustment |
---|---|---|---|
2 | 100 | 2 | (10) |
1 | 40 | 1 | (5) |
Sale volume levels are generated as:
Qty | Price | Equation |
---|---|---|
1 | 45 | Quantity = 1 (First level is always set to one) |
3 | 35 | Quantity = Previous |
It is possible that sale price levels that do not match the list price levels. For example, if the following prices are defined:
List Price Qty | List Price | Sale Price Qty | Sale Price |
---|---|---|---|
1 | 50 | 1 | 25 |
3 | 40 | 6 | 20 |
6 | 30 |
The PricingAdjustments
for an item quantity of six (6) is generated as:
List Price Qty | List Price Adjustment | Sale Price Qty | Sale Price Adjustment |
---|---|---|---|
2 | 100 | 2 | (50) |
3 | 120 | 3 | (45) |
1 | 30 | 1 | (10) |
And the resulting sales price is calculated to:
Qty | Price |
---|---|
1 | 25 |
3 | 25 |
6 | 40 |
ItemPriceSource Integration
The ItemPriceSource
is integrated with the pricing operation by passing a list of source objects into the pricing operation through the extra parameters map using a publicly defined key in atg.commerce.pricing.PricingConstants
.
Using the list of source objects, the ItemPricingEngine
generates a map in extraParameters
that maps each commerce item in the order to its matching ItemPriceSource
. This mapping operation occurs at the start of every pricing operation.
PricingTools
provides the generateItemPricingSourceMap
API for this mapping operation. Each item to be priced is matched to an ItemPriceSource
object using the isItemMatch
interface of ItemPriceSource
. If a match is found, the ItemPriceSource
currency code is verified to ensure that it matches the currency of the current pricing operation. If both conditions are met, the item is then mapped to the source object.
To map ItemPricingSources
to CommerceItems
, the algorithm that matches a CommerceItem
to an ItemPriceSource
is part of the ItemPriceSource
API. The isItemMatch
compares the productId
, skuId
, parentSkuId
and commerceItemClassType
properties of the CommerceItem
to that in the ItemPriceSource
. All values must match exactly for a successful comparison.
The resulting ItemPriceSourcesMap
is stored in the extra parameters map using a key that is declared in atg.commerce.pricing.PricingConstants
. Each calculator determines if there is a matching ItemPriceSource
for an item by performing a lookup in the ItemPriceSourcesMap
using the CommerceItemId
as the key.
The ItemPriceSourceHandler
interface defines the getItemPriceSource
method that provides a matching ItemPriceSource
object to the calculator during a pricing operation. A null return value means there is no ItemPriceSource
and the calculator should continue to price the item normally. If an ItemPriceSource
is returned, the calculator determines if it contains the necessary information for the calculator to price the item.
Each handler implements the shouldConsumeSource
method that determines if an ItemPriceSource
contains the necessary information to price the item. For example, the list price calculator needs a non-null value returned by the ItemPriceSource.getListPrice
method. If that value is null, the calculator cannot consume the ItemPriceSource
and will return false
from the shouldConsumeSource
method.
If shouldConsumeSource
returns false
, the calculator bypasses its normal processing completely and returns immediately. Only the calculators for which the ItemPriceSource
provides the required data will consume it during a pricing operation.