Pricing (and Entitlements)

The OOTB way to set up prices in B2B commerce is by using pricelists. Besides the price, they also drive the entitlements (aka the list of products your account can see). You can combine several pricelists and B2B commerce will take care of figuring out which price to display. OOTB the price for a product is in the price list item.

Key Definitions

Keyword Object Name Description
Account Group ccrz__E_AccountGroup__c Group multiple accounts together to provide pricing and/or entitlements.
Price Group ccrz__E_PriceGroup__c Alternative pricing for accounts. Primary used for contracts, relative (upgrade/downgrade), segmented and contextual based pricing.
Pricelist ccrz__E_PriceList__c Represents a grouping of products to control pricing and entitlements per currency
Pricelist item ccrz__E_PriceListItem__c A Price List entry. Contains a reference to the product and hold a price value
Product ccrz__E_Product__c B2B Commerce Product object (not to be confused with Product2)

Pricing/Entitlement flows

The most common path used to find the price list is via account groups:

  1. An account can have ONE account group
  2. An account group can have ONE or MORE pricelists.
  3. Each pricelist consist of ONE or MORE pricelist items.
  4. Each pricelist item refers to ONE product (aka SKU), and contains the price for it.

Pricing ERD

In the approach above, you can only have one account group per account.

Another common route is via price groups:

  1. An account can have ONE or MORE price groups (via the price group account object)
  2. A price group can have ONE or MORE pricelists (via the price group price list object)
  3. Each pricelist consist of ONE or MORE pricelist items.
  4. Each pricelist item refers to ONE product (aka SKU), and contains the price for it.

Pricing ERD Alt

This is the typical approach for Contract Pricing (where the same account may have multiple contracts, each with different terms of what they can buy and at which price)

Effective Account

WIP

Contract Pricing

WIP

Extending how the price/entitlements are determined

If this OOTB approach is not how your business sets the prices for its users (not standard, but not uncommon), you will have to either:

  1. Extend the way the pricelists are determined (e.g. based on an additional condition, like some attribute in the User object)
  2. Extend how the price itself is calculated (e.g. external pricing, custom pricing rules, etc.)

B2B commerce golden rule:

  • Try to use configuration settings first.
  • Use extensions when it makes sense.
  • Avoid creating new custom classes that duplicates functionality (prefer extensions).

Pricelist entitlements (example)

Use case

On the day before, during and after the contact's birthday, offer special prices and products (e.g. cakes, party supplies, discounts in drinks).

Why an extension?

If we were to do it without code, this approach would require one account per user. Or maybe do some outside the box thinking and email coupons and the list of products under the birthday promotion. Either way, this may require quite a lot of records...and hopefully they were set up correctly (and you don't want to use additional coupons that day).

With an extension, you can set a pricelist for the birtday range and you're done.

General approach

  1. Get the birthdate of the logged contact
  2. If the birthday is greater than Yesterday, but less than Tomorrow, find the current birthday pricelist and add it to the list of entitled pricelists.

Design

  1. To keep it simple, we'll add a checkbox field to the pricelist object, to indicate whether they are birthday pricelists or not. We'll take advantage of the one to many relationship between the pricelist and pricelist items to display the products and their prices, as well as the date validation and sorting strategies (e.g. either by best price or by sequence)
  2. Create an extension class to the ccLogicPLEntLists class (overriding fetchPriceGroupPriceListIDs and/or fetchAccountGroupPriceListIDs) to return the additional birthday pricelists if applicable.
  3. In your org, go to CC Admin and replace the value of ccLogicPLEntLists under the Service Management option with your custom class.

Sample code

Coming Soon
1

Overriding Price (example)

Use case

On the day before, during and after the contact's birthday, offer 5% discount to all the products ordered.

Why an extension?

If we were to do it without code, the best solution would probably involve the use of a coupon. But you won't see the new price until you actually add it to the cart and apply it. And you won't be able to stack coupons (current OOTB limitation)

With an extension, you can see the discounted price when navigating through the product list.

General approach

  1. Get the birthdate of the logged contact
  2. If the birthday is greater than Yesterday, but less than Tomorrow, modify the prices shown in the storefront.

Design

  1. Create an extension class to the ccLogicProductPricing class (overriding determinePrice) to return the price -5% if applicable.
  2. In your org, go to CC Admin and replace the value of ccLogicProductPricing under the Service Management option with your custom class.

Sample code

Homework: find the contact's birthdate and determine whether the discount applies

global with sharing class ckz54_ccLogicProductPricing extends ccrz.ccLogicProductPricing{
    global override Map<String, Object> determinePrice(Map<String, Object> inputData) {
        Map<String, Object> retData;
        if (Test.isRunningTest()) {
            retData = new Map<String, Object>{
                //replace with your custom test data
            };
        } else {
            //calls the super of the method
            retData = super.determinePrice(inputData);
        }
        Decimal regularPrice=(Decimal)(retData.get('price'));
        ccrz.ccLog.log(LoggingLevel.DEBUG,'M:X:ckz54_LogicProductPricing:determinePrice:regularPrice',regularPrice);
        //TBD: check the birthday
        //note that this will also apply the discount on the base pricelist (you may want to avoid this)
        retData.put('price', (regularPrice*0.95).setScale(2, System.RoundingMode.HALF_EVEN));
        ccrz.ccLog.log(LoggingLevel.DEBUG,'M:X:ckz54_LogicProductPricing:determinePrice:retData',retData);
        return retData;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Demo

With the 5% applied to the regular price. E.g.

  • For 2800 5% discount results in 2660
  • For 2975, this is 2826.25

Pricing Extension -5%

Another alternative would be to extend ccServicePL (for the pricelist) and/or ccServicePLI (for the pricelist items) service classes, and leave the logic to the query itself, instead of the logic classes.


Extras

Pricing data in the CCRZ object (for Handlebars)

Consider using these CCRZ objects in your custom Handlebars templates or js code. They contain a lot of the pricing data you may need (and you can extend them if something is missing)

Page CCRZ object
PDP CCRZ.productDetailModel.attributes.product
PLP CCRZ.productListPageView.productItemsView.itemViews[x].model.attributes
Featured and Spotlight products CCRZ.views.spotView.model.attributes
Cart items CCRZ.cartDetailModel.attributes.ECartItemsS.models[x].attributes

The CCRZ object list above may not be 100% accurate and may vary depending on how you are using B2B commerce. Why this inconsistency? The above are not documented, but there are different js objects that could contain pricing data. Give this a try first, and if it doesn't work, look around using the browser's inspector tools

Contract Pricing Selector

WIP


Tags: pricing, entitlement, pricelist, ccLogicProductPricing, ccLogicPLEntLists, fetchPriceGroupPriceListIDs, fetchAccountGroupPriceListIDs, determinePrice, CC Account Group, CC Price Group

References: https://cloudcraze.atlassian.net/wiki/spaces/B2BDOCS410/pages/888998151/Pricing+and+Entitlements+Data