Tutorial 3: Extending Ecommerce
In the previous tutorial we covered the template hierarchy, the rendering process, and the basics of frontend development in Dynamicweb – and in this tutorial we will get our hands dirty by actually extending an area of Dynamicweb – Ecommerce.
Ecommerce is a huge and important area in Dynamicweb, and can be extended in a multitude of places:
- The Dynamicweb.Ecommerce.Notifications namespace contains a list of notifications you can subscribe to
- You can create custom providers for many functions, e.g.
- A PriceProvider provides Dynamicweb Ecommerce with an algorithm for retrieving a specific price for a product
- A DiscountExtenderBase allows you to create discount types with backend user input
- A CheckoutHandler handles the business logic required to complete an order
- A TaxProvider calculates tax rates for products
- A ShippingProvider calculates shipping rates
- A FeeProvider allows you to manipulate shipping fees
- Etc.
In this tutorial, we will be creating the following extensions:
- A notification subscriber which hooks into the shopping cart and removes order lines which don’t fulfill a particular criteria
- A custom price provider which reduces the price of a product in both the product catalog and the cart with 25% if it starts with a particular letter
We will also create a custom discount provider, to show how you use configurable add-ins to extend the Dynamicweb administration with custom input controls.
Alter order lines – a Notification Subscriber
In this example, we have a business where products are sold only in batches by the dozen. To ensure this we will always add a quantity to make the order line quantity dividable by 12:
Creating a custom PriceProvider
A PriceProvider provides Dynamicweb Ecommerce with an algorithm for retrieving a specific price for a product. It overrides the default algorithm of the system by returning a price.
It is up to you to decide how the price data is retrieved, cached and updated – and it’s perfectly possible to call external systems and query for price data.
The following rules apply for PriceProviders:
- Multiple PriceProviders are allowed.
- A PriceProvider should never returns a price if it does not have one for a particular situation. In the case that a PriceProvider cannot resolve a price, the system will call the next PriceProvider in the provider chain.
- If no PriceProvider exists for a price, the system will fall back to the DefaultPriceProvider (Product Price Matrix)
A price is the cost per unit of a product sold in a specific context. When the system asks a PriceProvider for a price, it calls the PriceProvider with a set of parameters that define the context. As the developer of a PriceProvider, you can choose to use one or more of the parameters to resolve the price. You can also ignore parameters that are not relevant for resolving a price. For example, if you are not concerned with the quantity parameter (because the price is not dependent of quantity), you can choose to overlook this parameter by returning the same price no matter what quantity the system asks for.
The context of the price is determined by the following parameters:
- Product – the product which the provider should resolve a price for
- Quantity – the number of product items which the provider should return a price for
- VariantID – the variant ID of the product variant which the provider should return a price for
- Currency – the currency of the requested price. Only return a price in this currency – if no price exists return NULL instead. The provider will be asked again with the default currency of the solution, and will use the currency exchange rates from your Ecommerce to calculate the correct price
- UnitID – the ID of the product unit which the provider should return a price for
- User – the Extranet user which the provider should return a price for
Dynamicweb expects a PriceProvider to return a PriceRaw object – a price without a context, consisting only of an amount and a currency (e.g. 10 USD). The context used to retrieve the price will be remembered by the system.
All sales tax and VAT calculations are done by the system, in accordance with the system configurations.
A PriceProvider simply needs to check whether the PricesInDbWithVAT bit is set in the configuration. If the bit is true, the PriceProvider always returns a price incl. VAT. If the bit is false, the PriceProvider always return prices without VAT.
The following is an example PriceProvider which reduces the price by half if the product name starts with ‘M’ and the user is logged in:
This provider returns a value, but it also supports methods for populating the cache, which is unique for the price provider type. This is because it is convenient to get all the prices you need in a single call to an external system, instead of calling the external system multiple times. To achieve this, implement the PreparePrices method. This method enables you to build a list of cached prices which you can use for making your custom calculation in FindPrice:
The PreparePrices method has two overloads: One that accepts a ProductCollection and another that accepts a dictionary of products and their quantities. The overload using a ProductCollection is typically called when dealing with a list of products as in the eCommerce Catalog. The overload using the dictionary with quantities is used in cases where you have a quantity, such as in the Ecommerce Cart.
Configurable Add-ins
Using the ConfigurableAddIn class, you can quickly extend certain areas of the Dynamicweb administration interface with custom input controls.
For instance, you may want to use custom input when calculating a discount – e.g. specify a country or an age group for which a discount should apply – and this is not possible using the standard functionality in Dynamicweb.
Configurable add-ins can be used in a number of providers, e.g.:
- CheckoutHandler
- FormSaveProviders
- DiscountExtenderBase
- The Live Integration add-in
- Scheduled Task add-ins
- Etc.
For a full list, please see the API documentation for the ConfigurableAddin class under Inheritance Hierarchy.
In this tutorial we will be creating a custom discount which awards a discount to users based on the day of their registration.
DiscountExtenderBase
Using the DiscountExtenderBase you can extend on the default settings of a discount. It handles both product discounts and order discounts. This is the base implementation:
You should notice
- A public class that inherits from a base class.
- The override method DiscountValidForProduct()
- The override method DiscountValidForOrder
- The overall use of boolean to determine if the discount should be granted
In the following example you will find a discount that can be triggered on two occasions:
- If day/month of users registration date (CreatedOn) matches current day/month, the discount will be granted.
- If the weekday of today matches that of the selected, the discount will be granted.
With the above code implemented, the new discount is now available to be selected from the administration alongside with the standard discount option settings: (Figure 5.3).
What you’ve learned – and what’s next
In this tutorial, you’ve learned the following:
- How to subscribe to a notification
- How to create a custom price provider
- How to use configurable add-ins to create input controls in the administration interface
In the next tutorial you will be learn about the flexible content type called items and how to work with them. In particular, you will be learning about defining items code-first – by creating a public .NET class which inherits from the ItemEntry base class.