Price calculation customization
Features described on this page require the Xperience by Kentico Advanced license tier.
The price calculation service provides several customization options to fit your specific business requirements. You can extend the core data transfer objects, modify existing calculation steps, or add entirely new steps to the calculation pipeline.
Extend data transfer objects
To customize a data transfer object, create a new class that inherits from the respective object, add your custom fields, and use the custom class in your calculation steps and services. When you extend these classes, you also need to update any related classes or services that utilize them to ensure compatibility with your custom fields.
The following objects are available for extension:
PriceCalculationRequestPriceCalculationResultPriceCalculationRequestItemPriceCalculationResultItemProductIdentifierProductData
The following example demonstrates extending ProductData with custom fields and creating corresponding result types that enforce type safety through generics.
// Custom ProductData with additional fields
public record CustomProductData : ProductData
{
public string TaxCategory { get; init; }
public bool IsTaxExempt { get; init; }
}
// Custom result item that enforces the CustomProductData type
public record CustomPriceCalculationResultItem : PriceCalculationResultItem<CustomProductData>
{
}
// Custom result that uses the custom result item type
public record CustomPriceCalculationResult : PriceCalculationResult<CustomPriceCalculationResultItem>
{
}
These custom types can be used when modifying calculation steps or implementing custom steps.
Modify existing calculation steps
You can modify the behavior of existing calculation steps to implement custom pricing logic without creating entirely new steps. When you register a custom step implementation, it automatically replaces the default step implementation in the calculation pipeline.
The following calculation step interfaces can be overridden:
|
Interface |
Description |
|
|
Loads product information and pricing data using the given IProductDataRetriever implementation. |
|
|
Evaluates and applies catalog promotions to individual products. |
|
|
Calculates line subtotals after catalog discounts. |
|
|
Calculates shipping costs. |
|
|
Evaluates and applies order-level promotions. |
|
|
Calculates applicable taxes. |
|
|
Calculates final line totals including taxes. |
|
|
Calculates the order total price. |
|
|
Calculates the final grand total. |
Override a calculation step
Create a custom class that implements the interface for the calculation step you want to modify. Add the
Executemethod with your custom logic.Register your implementation in the dependency injection container. When you register a custom step implementation, it automatically replaces the default step in the calculation pipeline:
C#Program.cs// Register with open generics to replace the default implementation builder.Services.AddTransient(typeof(IYourCalculationStep<,>), typeof(CustomCalculationStep<,>));
For a complete example of overriding a calculation step, see Implement tax calculation, which demonstrates replacing the default tax step with custom tax logic.
Implement custom calculation steps
Custom calculation steps allow you to add specialized pricing logic such as volume-based discounts, loyalty program benefits, handling fees, gift wrapping charges, or complex promotional rules. Custom steps can be inserted anywhere in the calculation pipeline by creating a custom steps provider.
Create a custom step
Add a custom class that implements
IPriceCalculationStep<TPriceCalculationRequest, TPriceCalculationResult>. In theExecutemethod, define your custom logic to modify the calculation result based on the calculation request.C#Custom calculation step implementationpublic sealed class CustomCalculationStep<TRequest, TResult> : IPriceCalculationStep<TRequest, TResult> where TRequest : PriceCalculationRequest where TResult : PriceCalculationResult { public Task Execute(IPriceCalculationData<TRequest, TResult> calculationData, CancellationToken cancellationToken) { // Custom calculation logic return Task.CompletedTask; } }Register your implementation in the dependency injection container to make it available to the price calculation service.
C#Program.csbuilder.Services.AddTransient<IPriceCalculationStep<,>, CustomCalculationStep>();Create a custom calculation steps provider to include your new step in the calculation pipeline.
Create a custom steps provider
A custom steps provider allows you to control which steps execute and in what order. You can reorganize the default pipeline, add custom steps, or conditionally include steps based on request properties such as the calculation mode.
When you register a custom implementation of IPriceCalculationStepsProvider, you need to ensure that all required calculation steps are included, including the default calculation steps. If any required step is omitted, the price calculation service may not function correctly, leading to incomplete or incorrect price calculations.
Create a class that implements
IPriceCalculationStepsProvider<TPriceCalculationRequest, TPriceCalculationResult>. Inject all default and custom calculation steps through the constructor.C#Custom calculation steps providerpublic class CustomCalculationStepsProvider : IPriceCalculationStepsProvider<PriceCalculationRequest, PriceCalculationResult> { private readonly IProductDataLoaderPriceCalculationStep<PriceCalculationRequest, PriceCalculationResult> productDataLoaderStep; // ... public CustomCalculationStepsProvider( IProductDataLoaderPriceCalculationStep<PriceCalculationRequest, PriceCalculationResult> productDataLoaderStep, // ... ) { this.productDataLoaderStep = productDataLoaderStep; // ... } // ... }Implement the
Getmethod to return the calculation steps in the desired order. The method receives the calculation request, enabling conditional step selection based on properties like the calculation mode.C#Get method implementation// The Get method receives the request to enable conditional step selection based on calculation mode public IEnumerable<IPriceCalculationStep<PriceCalculationRequest, PriceCalculationResult>> Get( PriceCalculationRequest request) { // Runs for all modes (Catalog, ShoppingCart, Checkout) yield return productDataLoaderStep; yield return catalogPromotionStep; yield return lineSubtotalsStep; // Determines steps for ShoppingCart and Checkout modes only if (request.Mode == PriceCalculationMode.ShoppingCart || request.Mode == PriceCalculationMode.Checkout) { // Adds shipping only in Checkout mode if (request.Mode == PriceCalculationMode.Checkout) { yield return shippingStep; } yield return orderPromotionStep; // Inserts custom step after order promotions yield return customStep; yield return taxStep; yield return lineTotalStep; yield return orderTotalStep; yield return orderGrandTotalStep; } }Register the custom steps provider to replace the default calculation pipeline. When you register your custom steps provider, the calculation service automatically detects and uses it, executing your custom step during calculations.
C#Program.csbuilder.Services.AddTransient<IPriceCalculationStepsProvider<PriceCalculationRequest, PriceCalculationResult>, CustomCalculationStepsProvider>();
Custom calculation modes
PriceCalculationMode can be extended with custom values. You can create custom modes by instantiating new PriceCalculationMode objects with custom names. Handle custom modes in your custom steps provider’s Get method to control which calculation steps execute for each mode.